Word Count 程式
GitHub位址:https://github.com/MansonYe/Word-Count
一、項目簡介
Word Count 是用以統計文本檔案的字元數、單詞數和行數的常用工具。
二、功能分析及實作情況
· 基本功能:
統計file.c的字元數(實作)
統計file.c的單詞數(實作)
統計file.c的行數(實作)
· 拓展功能:
遞歸處理目錄下符合類型的檔案(實作)
顯示代碼行、空行和注釋行的行數(實作)
支援通配符(* , ?)(實作)
· 進階功能:
支援GUI界面并顯示詳細資訊(待實作)
· 定義:
字元:可顯示的ASCII碼字元,是以不包括空格和‘\n’等控制字元
單詞:由一串連續英文字母組成,遇到英文以外為單詞的分隔
行:每行以分行符或結束符為标志,分為三種:
空行:本行隻由非顯示字元組成,若有代碼,則不超過一個可顯示字元
代碼行:本行包括多于一個字元的代碼
注釋行:本行不是代碼行,且包括注釋
·例子:
如圖為一個典型的C語言文本檔案
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CM4MjN0gDN0kDMxATNxkDM4EDMy8CX5ADOxAjMvw1bm5WavwVbvNmLvtWdiVnYuU2Zh1Wavw1LcpDc0RHaiojIsJye.png)
所有顯示的字元均為納入字元計算中:
如2行有19個字元
以非英文字母分隔單詞:
如10行單詞數為3,但7行單詞數為0
包含多于一個代碼的行為代碼行:
如10、14、21行等均為代碼行
不是代碼行且包含注釋為注釋行:
如4、5、22、24行等,6行因為在文檔型注釋中顧算注釋行
沒有顯示字元或隻有一個代碼的行為空行:
如1、3、15行,但6行在文本注釋中是以不算作空行,算作注釋行
三、PSP
PSP
Personal Software Process Stages
預估耗時(分鐘)
實際耗時(分鐘)
Planning
計劃
30
30
Estimate
· 估計這個任務需要多少時間
10
10
Development
開發
480
600
Analysis
· 需求分析 (包括學習新技術)
60
70
Design Spec
· 生成設計文檔
5
5
Design Review
· 設計複審 (和同僚稽核設計文檔)
30
50
Coding Standard
· 代碼規範 (為目前的開發制定合适的規範)
10
10
Design
· 具體設計
60
65
Coding
· 具體編碼
480
540
Code Review
· 代碼複審
60
75
Test
· 測試(自我測試,修改代碼,送出修改)
60
120
Reporting
報告
120
120
Test Report
· 測試報告
30
60
Size Measurement
· 計算工作量
10
5
Postmortem & Process Improvement Plan
· 事後總結, 并提出過程改進計劃
30
60
四、解題思路及功能實作:
字元統計:
周遊文檔字元,通過排除非顯示字元,統計顯示字元數量;
單詞統計:
周遊文檔字元,利用變量記錄字元是否為英文字母狀态,統計進入該狀态次數即為單詞詞數
行數統計:
周遊文檔以行為機關的字元串,周遊次數即為行數
特殊行數統計:
周遊文檔以行為機關的字元串,再利用指針周遊字元串;首先判斷是否為代碼行(優先級最高),其次判斷是否為注釋行,由于三種行互斥,顧空行數為總行數減去前兩者。通過變量記錄狀态以判斷代碼行和注釋行。
通過遞歸實作檔案周遊:
利用_findfirst,_findnext,_findclose函數實作目前檔案夾檔案周遊,通過attrib屬性确定檔案夾,通過加工字元串,将新字元串作為新參數調用自身實作通過遞歸進入下一級目錄。同時通過加工字元串在路徑後加上檔案名作為參數傳遞給統計函數進而實作每個檔案的Word Count。
五、關鍵代碼:
基本功能:
int CodeCount(char *Path) { //計算字元個數
FILE*file = fopen(Path, "r");
assert(file!= NULL); //若檔案不存在則報錯
charcode;int count = 0;while ((code = fgetc(file)) != EOF) //讀取字元直到結束
count+= ((code != ‘ ‘) && (code != ‘\n‘) && (code != ‘\t‘)); //判斷是否是字元
fclose(file);returncount;
}int WordCount(char *Path) { //計算單詞個數
FILE*file = fopen(Path, "r");
assert(file!=NULL);charword;int is_word = 0; //用于記錄字元是否處于單詞中
int count = 0;while ((word = fgetc(file)) !=EOF) {if ((word >= ‘a‘ && word <= ‘z‘) || (word >= ‘A‘ && word <= ‘Z‘)) { //判斷是否是字母
count += (is_word == 0);
is_word= 1; //記錄單詞狀态
}elseis_word= 0; //記錄不處于單詞狀态
}
fclose(file);returncount;
}int LineCount(char *Path) { //計算行數
FILE*file = fopen(Path, "r");
assert(file!=NULL);char *s = (char*)malloc(200 * sizeof(char));int count = 0;for (; fgets(s, 200, file) != NULL; count++); //逐次讀行
free(s);
fclose(file);returncount;
}
特殊行數統計:
void AllDetail(char *Path) { //顯示空行, 代碼行,注釋行
FILE*file = fopen(Path, "r");
assert(file!=NULL);char *s = (char*)malloc(200 * sizeof(char));//申請空間
inti;int is_codeline = 0; //狀态記錄變量
int is_annoline = 0;int AnnoLock = 0;int AnnoFileLock = 0;int codecount = 0;int annocount = 0;int blankcount = 0;while (fgets(s, 200, file) != NULL) { //逐次取檔案中的行
for (i = 0; *(s+i) != ‘\0‘; i++) {if ( ( ( *(s+i) >= ‘a‘ && *(s+i) <= ‘z‘) || ( *(s+i) >= ‘A‘ && *(s+i) <= ‘Z‘) ) && AnnoFileLock == 0) {//判斷是否是代碼行
codecount += (is_codeline == 0 && AnnoLock == 0); //進入代碼行的時候代碼行加一
is_codeline = 1;
}if ( *(s+i) == ‘/‘ && *(s+i+1) == ‘/‘ && is_codeline == 0 && AnnoFileLock == 0){ //判斷是否為注釋行
annocount++;
AnnoLock= 1;
}if (*(s + i) == ‘/‘ && *(s + i + 1) == ‘*‘){ //判斷文檔注釋開始
AnnoFileLock = 1;
annocount-= is_codeline; //注釋在代碼後不算注釋行,是以減一
}if (*(s + i) == ‘*‘ && *(s + i + 1) == ‘/‘) { //判斷文檔注釋結束
AnnoFileLock = 0;
annocount+= (*(s + i + 2) == ‘\n‘); //注釋後換行情況
}
}
annocount+= AnnoFileLock; //注釋行結束時算作注釋行加一
blankcount++; //每一行結束計數加一,并清空狀态
is_codeline = 0;
is_annoline= 0;
AnnoLock= 0;
}free(s);
fclose(file);
blankcount= blankcount - codecount -annocount;
printf("codeline:%d, annoline:%d, blankline:%d\n", codecount, annocount, blankcount);
}
通過遞歸實作檔案周遊:
void Scan(char *Path, charType) {char *FileName =NULL;char *FileType =NULL;char Temp[30]; //用于暫存改變得字元串
longHead;struct_finddata_t FileData;int i = 0;
FileName=Path;while (*(Path + i) != ‘\0‘) { //找出檔案名和檔案類型的位置
if (*(Path + i) == ‘\\‘)
FileName= Path + i + 1;if (*(Path + i) == ‘.‘)
FileType= Path + i + 1;
i++;
}
strcpy(Temp, FileType);//調整字元串
*FileType = ‘*‘;*(FileType + 1) = ‘\0‘;
Head= _findfirst(Path, &FileData);
strcpy(FileType, Temp);//恢複字元串
do{if ( !strcmp(FileData.name, "..") || !strcmp(FileData.name, ".")) //去除前驅檔案路徑
continue;if (_A_SUBDIR == FileData.attrib) //是檔案夾
{
strcpy(Temp, FileName);//調整字元串
for (i = 0; *(FileData.name + i) != ‘\0‘; i++) {*(FileName + i) = *(FileData.name +i);
}*(FileName + i) = ‘\\‘;*(FileName + i + 1) = ‘\0‘;
strcat(Path, Temp);
Scan(Path, Type);
strcpy(FileName, Temp);//恢複字元串
}else//是檔案
{for (i = 0; *(FileData.name + i) != ‘.‘; i++);if (!strcmp(FileData.name + i + 1, FileType)) { //是指定類型的檔案
strcpy(Temp, FileName);
strcpy(FileName, FileData.name);//調整字元串
printf("%s:", FileData.name);
Run(Type, NULL, Path);//将位址及功能傳到啟動函數
printf("\n");
strcpy(FileName, Temp);//恢複字元串
}
}
}while (_findnext(Head, &FileData) == 0);
_findclose(Head);
}
啟動函數:
void Run(char Type, char Type2, char *Path) {switch(Type) {case ‘c‘: printf("code count: %d\n", CodeCount(Path)); break;case ‘w‘: printf("word count: %d\n", WordCount(Path)); break;case ‘l‘: printf("line count: %d\n", LineCount(Path)); break;case ‘a‘: AllDetail(Path); break;case ‘s‘: Scan(Path, Type2); break;default: printf("type input error"); break;
}
}
六、測試結果
字元數計算測試:
單詞數計算測試:
行數計算測試:
多種行數計算測試:
周遊功能測試:
測試用檔案:
test1:
test2:
c
test3:
word
test4:
#include#include
int main() //main method
{int i = 100 / 4;
printf("hello world");
return 0;
}//this is test file//this is test file with many kinds of annotations
test5:
another test file
asasas
七、小結
自己比較少按正常的順序進行項目的開發。這次的機會令我再次認識到做好事前分析和安排的重要性,根據安排進行開發,有效地提高了開發過程中的可見性。
一開始準備的時候考慮過使用其他語言,但設計到了分辨注釋行的時候,由于指針能發揮巨大作用最終還事選擇了C語言
寫程式的中途我學到了不少新知識,特别是在檔案周遊這一方面,并得到了一定程度的實踐經驗。
同時我也認識到自己關于測試方法知識的匮乏,雖然這次早早地就寫完了程式,但是為了搞測試項目,花了很多時間但最後還是隻能手動測試,還接近了時間限制,是以眼下應該先學會如何測試項目的正确使用方法。
經過這次作業,我認為隻有通過不斷地實踐與學習,才能夠結合知識與實踐,進一步提升自己的能力。
ps:第一次寫部落格,不知道原來能直接用html寫,下次應該會呈現得更好!
原文:https://www.cnblogs.com/mansonye/p/9649663.html