GitHub地址:https://github.com/dachai9/personal-project.git
1. WC 项目要求
-
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。 -
具体功能要求:
程序处理用户需求的模式为:wc.exe [parameter][file_name] -
基本功能列表:
wc.exe -c file.c //返回文件 file.c 的字符数。 --完成
wc.exe -w file.c //返回文件 file.c 的词的数目。 --完成
wc.exe -l file.c //返回文件 file.c 的行数。 --完成 -
扩展功能:
-s 递归处理目录下符合条件的文件。 --完成
-a 返回更复杂的数据(代码行 / 空行 / 注释行)。 --完成 (空行的格式控制字符只识别%d)- 空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
- 代码行:本行包括多于一个字符的代码。
- 注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释:在这种情况下,这一行属于注释行。
-
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。 --未完成
需求举例:
wc.exe -s -a .c
返回当前目录及子目录中所有.c 文件的代码行数、空行数、注释行数。
2. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 10 |
Development | 开发 | 540 | 985 |
· Analysis | · 需求分析 (包括学习新技术) | 20 | 20 |
· Design Spec | · 生成设计文档 | 20 | 5 |
· Design Review | · 设计复审 (和同事审核设计文档) | 5 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 15 | 20 |
· Design | · 具体设计 | 80 | 80 |
· Coding | · 具体编码 | 240 | 300 |
· Code Review | · 代码复审 | 60 | 300 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 240 |
Reporting | 报告 | 100 | 180 |
· Test Report | · 测试报告 | 60 | 80 |
· Size Measurement | · 计算工作量 | 10 | 40 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 60 |
合计 | 660 | 1175 |
3. 解题思路
- 需求分析:
- 基础:需要进行文件操作,fgetc,fopen,fclose;
- 扩展:_finddata_t handle,符号判断;
- c语言,文件操作方法;
- wc.exe [parameter][file_name]主函数参数;
4. 设计实现过程:
5. 代码说明
- 主函数:
int main(int argc, char *argv[]) { printf("传入参数:%d\n", argc); char para;//需求 char fpara[] = ""; char *func;//-需求 //判断输入多少需求 int k=0; for(k=1; k<=argc; k++) { func = argv[k]; if(func[0] != '-') { break; } } printf("\nk: %d\n", k); char *add = argv[k]; for(int j=k; j<argc; j++) { add = argv[j]; for(int i=1; i<k; i++) { func = argv[i]; para = func[1]; switch(para) { case 's': printf("\n"); recursion(add); break; case 'a': justify(add); break; case 'l': countLines(add); break; case 'w': countWords(add); break; case 'c': countChars(add); break; default: break; } } } }
- 行数统计
int countLines(char *fileName) { FILE *fp; fp = fopen(fileName, "r"); char ch; int cl = 0; if(fp == NULL) { printf("打开文件失败!\n"); } else { ch = fgetc(fp); if(ch == EOF) { printf("行数:0\n"); return 0; } while(ch != EOF) { if(ch != '\n') { ch = fgetc(fp); } else { cl++; ch = fgetc(fp); } } printf("行数:%d\n", ++cl); } fclose(fp); return cl; }
- 单词数统计
int countWords(char *fileName) { FILE *fp; fp = fopen(fileName, "r"); char ch; int cw = 0; int word = 0; if(fp == NULL) { printf("打开文件失败!\n"); } else { ch = fgetc(fp); if(ch == EOF) { printf("单词数:0\n"); return 0; } while(ch != EOF) { /*if(ch != ' ' && ch != '\n') { //putchar(ch); ch = fgetc(fp); }else { cw++; //printf("单词数:%d\n", cw); ch = fgetc(fp); }*/ if((ch == ' ' || ch == '\n') && word == 1) { word = 0; cw+=1; } else if(((ch >= 'a' && ch <= 'z') || ch >= 'A' && ch <='Z') && word == 0) { word = 1; if((ch = fgetc(fp)) == EOF) cw++; } //printf("%c", ch); ch = fgetc(fp); } printf("单词数:%d\n", cw); } fclose(fp); return cw; }
- 字符数统计
int countChars(char *fileName) { FILE *fp; fp = fopen(fileName, "r"); int cc = 0; char ch; if(fp != NULL) { ch = fgetc(fp); while(ch != EOF) { if(ch != '\n') { cc++; ch = fgetc(fp); } else { ch = fgetc(fp); } } printf("字数:%d\n", cc); } else { printf("打开文件失败!\n"); } fclose(fp); return cc; }
- 遍历文件(函数本身可处理*.c,但由于传入参数如果是*.c,代表一个一个文件传入,所以这里函数作用不明显)
int recursion(char *fpara) { long handle;//用于查找的句柄 struct _finddata_t fileinfo;//文件信息的结构体 handle = _findfirst(fpara, &fileinfo); if(-1 == handle) { printf("查找失败!"); system("pause"); return -1; } printf("%s\n", fileinfo.name); while(!_findnext(handle, &fileinfo)) { printf("%s\n", fileinfo.name); } _findclose(handle); return 0; }
- 特殊行判断(其中的zs_f 是判断是否出现/*)
int justify(char *fileName) { FILE *fp; fp = fopen(fileName, "r"); if(NULL == fp) { printf("打开文件失败!"); system("pause"); return 0; } int zs_f = 0; char ch_fp, ch_next;//ch_next:下一个字符 int k=0, d=0, z=0, f=0;//k: 空行 d:代码行 z:注释行 f:是否顶格 ch_fp = fgetc(fp); ch_next = fgetc(fp); while(ch_fp != EOF) { if(ch_fp == ' ' || ch_fp == '}' || ch_fp == '{') { ch_fp = ch_next; ch_next = fgetc(fp); } else { if((ch_fp == '\n') && f == 0) { k++; } else if(ch_fp == '/' && ch_next == '/' && f == 0) { z++; } else if(ch_fp == '/' && ch_next == '*' && f == 0) { z++; zs_f = 1; } if(zs_f == 1) { while(ch_fp != '*' || ch_next != '/') { if(ch_fp == '\n') { z++; } ch_fp = ch_next; ch_next = fgetc(fp); } zs_f = 0; } ch_fp = ch_next; ch_next = fgetc(fp); if(ch_fp == '{' || ch_fp == '}') { ch_fp = ch_next; ch_next = fgetc(fp); } if(f == 0) { f = 1; if(ch_next == ' ') { f = 0; } } if(f == 2) { if(ch_fp == '\n') { k++; } f=0; } if(ch_fp == '\n') { f = 2; if(ch_next == '\n') f=0; if(ch_next == EOF) k++; } } } d = countLines(fileName) - k - z; printf("\n空格行:%d\n代码行:%d\n注释行:%d\n", k, d, z); printf("文档结束!\n"); return 0; }
测试运行
-
基础部分:
-
扩展部分:
项目小结
对GitHub的使用还不熟练;学了新的知识:_finddata_t;提高了逻辑思维能力(特殊行判断);学会控制情绪。
花了这么长时间,说到底还是太菜,没有好好理清思路就上手,所以做好设计文档还是蛮重要的。
非常喜欢PSP,觉得这个计划表可以能鞭策自己再加努力,不管是对项目需求的分析分解还是对自己编程能力的提高。
还有就是自己预计的时间跟最终的时间相差了将近8小时!!!太夸张了。还是要充分了解需求才能好好估算时间。
来源:https://www.cnblogs.com/dachai/p/12559314.html