码云代码地址:
https://gitee.com/dxl123456/WordCount
1.项目需求
WordCount的需求:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
WordCount的要求:
(1)编写名为wc.c的源文件,cmd gcc -wc.c -o wc 生成 可执行程序wc.exe,该程序处理用户需求的模式为:
wc.exe [parameter] [input_file_name]
存储统计结果的文件默认为result.txt,放在与wc.exe相同的目录下。
wc -c file.c //返回文件 file.c 的字符数
wc -w file.c //返回文件 file.c 的单词总数
wc -l file.c //返回文件 file.c 的总行数
wc -o outputFile.txt //将结果输出到指定文件outputFile.txt
注意:
空格,水平制表符,换行符,均算字符。
由空格或逗号分割开的都视为单词,且不做单词的有效性校验,例如:thi#,that视为用逗号隔开的2个单词。
-c, -w, -l参数可以共用同一个输入文件,形如:wc.exe –w –c file.c 。
-o 必须与文件名同时使用,且输出文件必须紧跟在-o参数后面,不允许单独使用-o参数。
第一,确保可执行文件的名字统一为 wc.exe。
第二,确保生成的结果文件 result.txt 与可执行文件在同一目录下,生成文件时请使用相对路径!
一个示例组织目录如下所示:
/ WCProject ( 工程名字自行指定即可 )/ *.* (放置源代码,具体目录自行处理)/ BIN / *.*(exe运行需要的依赖库文件) / wc.exe / result.txt (运行exe后生成)
参数及其约定如下:
参数名字 |
参数意义 |
用法示例 |
-c[必选] |
文件的字符数 |
示例:wc.exe -c file.c [表示返回文件file.c的字符数,并存储在result.txt中] |
-w[必选] |
文件单词总数 |
示例:wc.exe -w file.c [表示返回文件file.c的单词数,并存储在result.txt中] |
-l[必选] |
文件行数 |
示例:wc.exe -l file.c [表示返回文件file.c的总行数,并存储在result.txt中] |
-o[必选] |
输出文件名 |
示例:wc.exe –c file.c -o outfile.txt [表示返回文件file.c的字符数,并存储在outfile.txt中] |
练习这个入门的程序让我对博客,码云,还有c语言的巩固,意义重大,相当于一个敲门砖。
2.PSP表格
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
30 |
10 |
· Estimate |
· 估计这个任务需要多少时间 |
300 |
360 |
Development |
开发 |
270 |
330 |
· Analysis |
· 需求分析 (包括学习新技术) |
120 |
40 |
· Design Spec |
· 生成设计文档 |
30 |
0 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
0 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
0 |
· Design |
· 具体设计 |
40 |
20 |
· Coding |
· 具体编码 |
40 |
70 |
· Code Review |
· 代码复审 |
30 |
10 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
100 |
50 |
Reporting |
报告 |
30 |
20 |
· Test Report |
· 测试报告 |
20 |
0 |
· Size Measurement |
· 计算工作量 |
15 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 |
10 |
|
合计 |
765 |
570 |
从上面的psp表格可以看出来,预计的时间和实际花费的时间是不一致的,测试环节更是差了一倍多,这个主要是程序简单,刚开始想的复杂了,以为要花很多时间去学习新知识,结果最后更多的时间花在了修改bug了。中间有些设计文档啊、代码复审啊之类的,更是看都没看,别提还去做一下了。小程序不必如此大费周折,不过养成好的习惯还是好的,建议走一遍流程,对自己做事情所要花费的时间有个数。
3、解题思路。
刚拿到题目,看了一遍要求,发现对文件的改写具体的代码忘记了,又想着这么经典的题肯定有人做了,于是上网搜了一下,看了大体的解决思路和file文件的基本用法,设计了一下输入输出的样子。就开始实践操作。
4、程序设计实现过程
一个main函数实现对输入命令的处理,调用相应的函数进行解决问题。
5、代码说明
5.1 运行环境 gcc+windows10+记事本
5.2代码详细设计
5.2.1将结果写入文件的函数
void writeToFile(char *fileName,char* feature,int num) { /*fileName 表示读取的文件名 * feature表示记录的内容 单词或者字母或者行数 */ FILE *f=NULL; f=fopen("./result.txt","a");//采用相对路径,作者创建在gcc\bin文件目录下,即wc运行程序一起的 if(f==NULL)//对空文件进行处理 { printf("failed when wrinting the count to file\n"); } //写入文件 fprintf(f,"%s,%s,%d\n",fileName,feature,num);//写入文件 fclose(f);//最后记得把文件关闭。 }
5.2.2 统计文件中的字符数的函数
f=fopen(file,"r");//以写的方式读取文件 while(!feof(f))//feof 检查是否到文件的结束位置 { c=fgetc(f); if(c!=' ' && c!= '\n' && c!='\t') num++; //判断是否为字符 } fclose(f); printf("c_num: %d",num); writeToFile(file,a,num); }
5.2.3 统计文件中单词个数的函数
ch=fgetc(f);//获取单个字符 if((ch>='a' &&ch <= 'z')||(ch>= 'A'&&ch<='Z') || ch=='_') aword=1;//如果一直是字符那末aword值为1 else if(aword)//检测到不是字符,转入这个循环,进行单词数目的加加 { cword++; aword=0; }
5.2.4 统计文件行数的函数
while(!feof(f)) { a=fgetc(f); if(a=='\n' || a=='\t') crl++;//直接检测是否为转行符号 } fclose(f); printf("crl_num: %d",crl); writeToFile(file,s,crl);
5.2.5 返回文件的空行数
int ch_num=0;//字符个数 while(!feof(f)) { ch=fgetc(f); if(ch=='\n') { if(ch_num<=1) b_num++; ch_num=0; } else if(ch!= ' '&& ch!= '\t' && ch!= '}' ) ch_num++; else if(ch=='}') b_num++;//只有一个‘}’的空行数 } fclose(f); printf("blankline_num: %d",b_num); writeToFile(file,a,b_num);
5.2.6 返回注释行的行数的函数
while(!feof(f)) { ch =fgetc(f); if(ch=='\n') { if(ch_num==2) note_num++; ch_num=0; } else if (ch=='/') ch_num++; else if(ch_num==1) { if(ch=='/') ch_num++; } } fclose(f); printf("noteline_num: %d",note_num); writeToFile(file,a,note_num);
5.2.7 返回代码的行数的函数
while(!feof(f)) { a=fgetc(f); if(flag==2) { flag=0;tag++; } else { if(a=='\n' && ch_num>1) { code_num++; ch_num=0; } else if(a!=' ' && a!='\n' && a!='\t' &&a!='/') ch_num++; else if(a=='/') flag++; } } fclose(f);printf("codeline_num: %d",code_num); writeToFile(file,s,code_num); }
5.2.8 查找文件的函数
void searchfile() { struct _finddata_t filefind; //注意空格和头文件 long handle; int t=0; if((handle=_findfirst("E:\\wordcount\\*.txt",&filefind))== -1L) { printf("file not found\n"); } else do { t++; printf("find file :%s\n",filefind.name); }while(_findnext(handle,&filefind)==0); _findclose(handle); printf("txt file_num : %d\n",t); }
5.2.9 main函数
int main(int argc, char* argv[]) { FILE *fp; while(1) { if((fp=fopen(argv[2],"r"))==NULL) { printf("FileNull\n\n\n"); scanf("%s%s%s",argv[0],argv[1],argv[2]); continue; } else if(!strcmp(argv[1],"-w")) word_num(argv[2]); else if(!strcmp(argv[1],"-c")) c_num(argv[2]); else if(!strcmp(argv[1],"-l")) crl_num(argv[2]); else if(!strcmp(argv[1],"-a")) { blankline_num(argv[2]); noteline_num(argv[2]); codeline_num(argv[2]); } else if(!strcmp(argv[1],"-s")) { searchfile(); } else printf("NullPoint\n"); printf("\n\n"); scanf("%s%s%s",argv[0],argv[1],argv[2]); } return 0; }
6、测试
6.1、函数测试
6.1.1测试代码
void testcnum(){ c_num("F:\\gcc\\bin\\a.txt");//可采用相对路径 } void testwordnum(){ word_num("F:\\gcc\\bin\\a.txt"); } void testcrlnum(){ crl_num("F:\\gcc\\bin\\a.txt"); } void testblanklinenum(){ blankline_num("F:\\gcc\\bin\\a.txt"); } void testnotelinenum(){ noteline_num("F:\\gcc\\bin\\a.txt"); } void testcodelinenum(){ codeline_num("F:\\gcc\\bin\\a.txt"); } void testsearchfile(){ searchfile(); } int main() { testcnum(); testwordnum(); testcrlnum(); testblanklinenum(); testnotelinenum(); testcodelinenum(); testsearchfile(); return 0; }
6.1.2测试结果
6.2、程序测试
6.2.1测试文档a.txt、b.txt
6.2.2测试结果
6.2.3.结果存于文档result.txt
7、心得体会
写代码之前的详细设计要有,七分文档三分代码,没有充分的计划,逻辑结构没有建立就直接写代码,反而会事倍功半。不会的就查,不懂得就实践,做一个软件人员,没有这种精神是不行的。对于测试,以前觉得没必要,程序能运行就好了,还管什么测试。学习这段实际,发现一些bug出现都是因为某个函数的错误导致后面部分的错误。如果代码庞大一点,这个后期找就很麻烦了,为了给自己省事还是测试一下,好在也有专门的测试工具。
8、参考文献链接
参考博客:http://www.cnblogs.com/silvercv/p/9692483.html
相对路径和绝对路径:https://www.cnblogs.com/vranger/p/3820783.html