WordCount
写在最前:
参考17079张武科同学的代码,故博文中形如详细代码设计等部分引用了张武科的博文,具体引用部分不详细标出,其他部分仍为本人原创
github地址
https://github.com/mrlandiao/wc
PSP表格
PSP2.1 |
PSP阶段 |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
25 |
45 |
· Estimate |
· 估计这个任务需要多少时间 |
25 |
35 |
Development |
开发 |
700 |
1000 |
· Analysis |
· 需求分析 (包括学习新技术) |
150 |
200 |
· Design Spec |
· 生成设计文档 |
10 |
20 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
35 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
20 |
· Design |
· 具体设计 |
60 |
70 |
· Coding |
· 具体编码 |
600 |
700 |
· Code Review |
· 代码复审 |
100 |
150 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
200 |
200 |
Reporting |
报告 |
80 |
100 |
· Test Report |
· 测试报告 |
30 |
40 |
· Size Measurement |
· 计算工作量 |
10 |
20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
15 |
25 |
合计 |
2045 |
2660 |
解题思路
参考上学期编写的词法分析和语法分析程序的基本功能,对其进行修改,得到main函数。利用控制台执行exe文件,通过传参的不同来实现所要求的功能
程序设计实现过程
本项目采用C语言实现,除main函数外,还有多个自定义函数,其中两个最主要的函数为count()和none(),count()用来统计单词数、行数、注释行数,none()用来统计空行数。
main()函数根据传入的形参的不同,完成不同的输出。其余自定义行数比较简单,下面代码说明中会以isNewline()函数为例进行解释。
代码说明
count()函数用以统计单词数、行数、注释行数,函数中有单词判断,没检测出一个单词word_num+1;每遇到一个'\n',line_num+1;没有到注释行,note_num+1,如果是/*note*/注释,在主是内部没出现一次‘\n’,note_num+1。
int count()//统计单词数、行数、注释行数 { get(); clearToken(); while (isSpace(ch) || isNewline(ch) || isTab(ch) || isComma(ch)) { if (isNewline(ch)) line_num++; get(); } if (isStar(ch)) { catToken(ch); } else if (isDivi(ch)) { catToken(ch); get(); if (isStar(ch)) { note_num++; catToken(ch); do { do { get(); if (isNewline(ch)) { note_num++; line_num++; } } while (!isStar(ch)); do { get(); if (isNewline(ch)) { note_num++; line_num++; } if (isDivi(ch)) { catToken(ch); return 0; } } while (isStar(ch)); } while (!isStar(ch)); } if (isDivi(ch)) { catToken(ch); do { get(); } while (!isNewline(ch)); note_num++; } if (isNewline(ch)) line_num++; } else if (isEnd(ch)) return 0; else { while (!(isSpace(ch) || isNewline(ch) || isTab(ch) || isComma(ch) || isDivi(ch))) { catToken(ch); get(); } retract(); if(strcmp(stopWord, "") == 0 || strcmp(token,stopWord)!=0) word_num++; } return 0; }
以isNewline()为例,解释简单自定义函数:
bool isNewline(char a) { if (a == '\n') return true; return false; }
none()函数,根据两个‘\n’中是否小于或等于一个字符来判断是否为空行,满足条件则none_num+1:
void none()//统计空行数 { int i,j = 0; for (i = 0;; i++) { if (art[i] == EOF) break; if (art[i] != '\n'&& art[i] != '\t'&& art[i] != ' ') { j++; } if (art[i] == '\n') { if (j <= 1) none_num++; j = 0; } } }
main()函数,由于功能是逐步添加的,加上自己的代码习惯有待提高,导致main()函数被修改的过于冗杂。重要思想:利用数组flag[7],每一个元素对应一个功能(包括字符数、单词数输出等),为1时表示对应功能执行,反之不执行。
int main(char argc, char* argv[]) { if (argc<3) printf("请在控制台输入正确命令!\n"); else { if (strcmp(argv[0], "wc.exe") != 0) printf("可执行文件名输入错误!\n"); else { for (int i = 1; i<argc; i++) { if (strcmp(argv[i], "-c") == 0) { flag[0] = 1; } else if (strcmp(argv[i], "-w") == 0) { flag[1] = 1; } else if (strcmp(argv[i], "-l") == 0) { flag[2] = 1; } else if (strcmp(argv[i], "-o") == 0) { flag[3] = 1; } else if (strcmp(argv[i], "-s") == 0) { flag[4] = 1; } else if (strcmp(argv[i], "-a") == 0) { flag[5] = 1; } else if (strcmp(argv[i], "-e") == 0) { flag[6] = 1; } } } } if (flag[4] == 1) { _finddata_t sFind; long lResult = 0; lResult = _findfirst("*.c", &sFind); if (lResult == -1) { printf("没有找到文件。"); return 0; } int i=0,len; do { len = strlen(sFind.name); strcpy_s(filesName[i], len + 1, sFind.name);//将所有读到的保存到filesname file_num++; i++; } while (_findnext(lResult, &sFind) != -1); for (int j = 0; j < file_num; j++) { fopen_s(&fp, filesName[j], "r"); if (fp == NULL) printf("error!\n"); int i = 0; do//获取文件内容 { ch = fgetc(fp); art[i++] = ch; } while (ch != EOF); fclose(fp); char_num = i; if (flag[6] == 1)//获取停用词 { fopen_s(&fp, "stopList.txt", "a"); fgets(stopWord, 20, fp); fclose(fp); } do//核心统计 { count(); } while (!isEnd(ch)); none(); if (flag[0] == 1) printf("%s,字符数: %d \n", filesName[j], char_num); if (flag[1] == 1) printf("%s,单词数: %d \n", filesName[j], word_num); if (flag[2] == 1) printf("%s,总行数: %d \n", filesName[j], line_num); if (flag[3] == 1) { fopen_s(&fp, argv[argc - 1], "a"); if (flag[0] == 1) fprintf(fp, "%s,字符数: %d \n", filesName[j], char_num); if (flag[1] == 1) fprintf(fp,"%s,单词数: %d \n", filesName[j], word_num); if (flag[2] == 1) fprintf(fp,"%s,总行数: %d \n", filesName[j], line_num); if (flag[5] == 1) fprintf(fp, "%s,代码行/空行/注释行: %d/%d/%d \n", filesName[j], line_num - note_num - none_num, none_num, note_num); fclose(fp); } if (flag[5] == 1) printf("%s,代码行/空行/注释行: %d/%d/%d \n", filesName[j], line_num - note_num - none_num, none_num, note_num); strcpy_s(stopWord,1,""); for (int i = 0; i < 1000; i++) art[i] = '\0'; sta = art; char_num = 0; word_num = 0; line_num = 0; note_num = 0; none_num = 0; } } else { fopen_s(&fp, "file.c", "r"); if (fp == NULL) printf("error!\n"); int i = 0; do { ch = fgetc(fp); art[i++] = ch; } while (ch != EOF); fclose(fp); char_num = i; if (flag[6] == 1) { fopen_s(&fp, "stopList.txt", "r"); fgets(stopWord, 20, fp); fclose(fp); } do { count(); } while (!isEnd(ch)); none(); if (flag[0] == 1) printf("file.c,字符数: %d \n", char_num); if (flag[1] == 1) printf("file.c,单词数: %d \n", word_num); if (flag[2] == 1) printf("file.c,总行数: %d \n", line_num); if (flag[3] == 1) { fopen_s(&fp, argv[argc - 1], "w"); if (flag[0] == 1) fprintf(fp, "file.c,字符数: %d \n", char_num); if (flag[1] == 1) fprintf(fp, "file.c,单词数: %d \n", word_num); if (flag[2] == 1) fprintf(fp, "file.c,总行数: %d \n", line_num); if (flag[5] == 1) fprintf(fp, "file.c,代码行/空行/注释行: %d/%d/%d \n", line_num - note_num - none_num, none_num, note_num); fclose(fp); } if (flag[5] == 1) printf("file.c,代码行/空行/注释行: %d/%d/%d \n", line_num - note_num - none_num, none_num, note_num); } printf("\n"); system("pause"); return 0; }
测试设计过程
利用“wc.bat”脚本程序,批量处理,测试用例如下:
测试结果为
测试数据集是我自己随意输入的,详情参见同目录下file.c文件,测试输出结果我已经检查,确认无误。需要说明的是其中仅包含 “ { ” 的行算作空行,不算作代码行。
另外,当“-s”和“-o”命令同时输入时,写入文件之前不会清空文件;仅包含“-o”命令而不包含“-s”命令时,写入文件之前会清空文件。
参考文献
1.https://www.cnblogs.com/saolv/p/7793379.html
2.https://www.cnblogs.com/chengxs/p/5309215.html
3.https://www.cnblogs.com/MrZhang145689/p/8613206.html