WordCount程序
Github项目地址:https://github.com/SivanWong/WordCount.git
一、程序说明
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
具体功能要求:
程序处理用户需求的模式为:
wc.exe [parameter] [file_name]
基本功能列表:
wc.exe -c file.c //返回文件的字符数 wc.exe -w file.c //返回文件的词的数目 wc.exe -l file.c //返回文件的行数
扩展功能列表:
wc.exe -a file.c //返回文件的更复杂的数据(代码行 / 空行 / 注释行)
进一步说明:
[parameter]:指令;
[file_name]:文件的路径;
-c:返回文件中的字符数,字符仅包含英文字符及数字字符;
-w:返回文件的英文单词数;
-l:返回文件的非空行数,非空指至少含有一个除空格、制表符、换行符以外的字符;
-a:返回文件的更复杂的数据(代码行 、空行 、注释行);
其中:
空行:全是空格、制表符、换行符和只含有‘{’或者‘}’的一行,对于空文件空行数为1;
代码行:含有两个及以上英文、数字字符的一行,不包含注释;
注释行:包含单行注释及多行注释;
二、PSP——预估耗时
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
Planning |
计划 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
Development |
开发 |
720 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
· Design Spec |
· 生成设计文档 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
· Design |
· 具体设计 |
30 |
· Coding |
· 具体编码 |
480 |
· Code Review |
· 代码复审 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
Reporting |
报告 |
50 |
· Test Report |
· 测试报告 |
30 |
· Size Measurement |
· 计算工作量 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 |
合计 |
|
800 |
三、解题思路
刚拿到题目时先思考用什么语言,由于平时接触得更多的是web前端开发,所以都不太熟练,其中对都学习过的java和c语言中,对c语言更为熟悉,因此选择c语言实现。
其次开始思考怎么写:
首先有对文件的输入,这涉及到如打开及读取文件的数据;
然后是如何根据输入的不同命令使用不同的函数;
最后设计了一下界面,使主界面能循环使用。
四、设计实现过程
五、代码说明
1、头文件
#include#include #include
2、主函数
思路:把输入的指令分成三组,存进三个不同的数组中。第一组为wc.exe,暂时无用;第二组为指令,作为判断来使用不同函数;第三组为文件的相对路径,是作为参数传送到各个函数中的。
int main(){ char str1[6], str2[2], str3[20]; char c; int CharCount(char *filename); int WordCount(char *filename); int LineCount(char *filename); int ComplexCount(char *filename); //形成一个循环 do{ system("cls"); printf("\n/**************欢迎使用WordCount**************/\n\n"); printf("(格式为:wc.exe [parameter] [file_name])\n\n"); printf("请输入用户命令:"); while((c=getchar())!='\n'){ scanf("%s%s%s",str1,str2,str3); //分组存储输入的指令 } if(str2[1]=='c'||str2[1]=='w'||str2[1]=='l'||str2[1]=='a'){ switch(str2[1]){ case 'c':CharCount(str3); system("pause"); break; case 'w':WordCount(str3); system("pause"); break; case 'l':LineCount(str3); system("pause"); break; case 'a':ComplexCount(str3); system("pause"); break; } }else{ printf("\n暂不支持%c命令\n",str2[1]); } }while(str2[1]=='c'||str2[1]=='w'||str2[1]=='l'||str2[1]=='a'); getchar(); return 0; }
3、CharCount()
思路:此函数用来返回文件的字符数。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,并判断是否为数字字符或者英文字符,是则计数+1,不是则继续读入下一个字符,不断循环,一直到读入文件结束符为止。
// 返回文件的字符数 int CharCount(char *filename){ FILE * fp; int charCount=0; char c; //先判断文件能否打开 if((fp=fopen(filename,"r"))==NULL){ printf("无法打开文件\n"); }else{ while(!feof(fp)){ c=fgetc(fp); //读入文件中的字符 if(isalnum(c)){ //判断字符是否为英文、数字字符 charCount++; } } printf("\n该文件的字符数为:%d\n\n",charCount); } return charCount; }
4、WordCount()
思路:此函数返回文件的单词数。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,当读入字符为英文字符时,word记为1作为标记一个单词的开始,继续读入下一个字符。一直到读入字符不为英文字符、连接符为止。不断循环,一直到读入文件结束符为止。
//返回文件的单词数 int WordCount(char *filename){ FILE * fp; int wordCount=0, word=0; char c; if((fp=fopen(filename,"r"))==NULL){ printf("无法打开文件\n"); }else{ c=fgetc(fp); while(c!=EOF){ if(isalpha(c)) //判断字符是否为英文字符 word=1; //标记为一个单词的开始 else if(c=='-'&&word==1){ word=1; }else{ if(word==1){ wordCount++; word=0; //一个单词结束,重置为0 } } c=fgetc(fp); } if(c==EOF&&word==1) //为了防止文件结束符前没有除英文字符及连接符以外的字符导致没有计数 wordCount++; printf("\n该文件的单词数为:%d\n\n",wordCount); } return wordCount; }
5、LineCount()
思路:此函数返回文件的行数,与计算文件单词数差不多。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,当读入字符不为空格、换行符、制表符时,line记为1作为标记一行的开始,继续读入下一字符。直到读入字符为换行符为止。不断循环,一直到读入文件结束符为止。
//返回文件的行数 int LineCount(char *filename){ FILE * fp; int lineCount=0, line=0; char c; if((fp=fopen(filename,"r"))==NULL){ printf("无法打开文件\n"); }else{ c=fgetc(fp); //读入文件中的字符 while(c!=EOF){ if(c!=' '&&c!='\t'&&c!='\v'&&c!='\r'&&c!='\n') line=1; //标记一行的开始 else if(c=='\n'){ if(line==1){ lineCount++; line=0; //一行结束,重置为0 }else line=0; } c=fgetc(fp); } if(c==EOF && line==1) //为了防止最后一行没有换行而没读到换行符导致没有计数 lineCount++; printf("\n该文件的行数为:%d\n\n",lineCount); } return lineCount; }
6、ComplexCount()
思路:返回文件更复杂的数据(空行、代码行、注释行)。主函数调用该函数时,若符合条件即文件可以打开,就会继续调用其他函数,来计算文件的空行、代码行、注释行。而此函数则会利用调用其他函数得到的数据来打印出相应的信息。
7、EmptyLineCount()
思路:此函数返回文件的空行行数,对于空文件有一行空行。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,当读入字符不为空格、换行符、制表符、’{‘、’}’时,empty记为0作为标记这一行不是空行,继续读入下一字符。直到读入字符为换行符为止。不断循环,一直到读入文件结束符为止。
//返回文件的空行行数 int EmptyLineCount(char *filename){ FILE * fp; char c; int empty=1, lineCount=0; fp=fopen(filename,"r"); c=fgetc(fp); while(c!=EOF){ if(c!=' '&&c!='t'&&c!='\v'&&c!='\r'&&c!='\n'&&c!='{'&&c!='}') empty=0; //标记这不是空行 else if(c=='\n'){ if(empty==0) empty=1; //重置为1 else{ lineCount++; empty=0; } } c=fgetc(fp); } if(c==EOF&&empty==1) //为了防止最后一行没有换行而没读到换行符导致没有计数 lineCount++; return lineCount; }
8、CodeLineCount()
思路:此函数返回文件的代码行行数,而只有一个字符的不算代码行。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,当读入字符为英文字符、数字字符时,判断code是否为1,是则标记codeline为1,代表这是代码行,code重置为0;否则标记code为1,这是这行的第一个有效字符。当读入字符为换行符且codeline为1时,计数+1,code、codeline重置为0.。若读入字符为’/’,则继续读入下一字符来判断是否为注释行,是则一直往下读入,不需要计数。不断循环,一直到读入文件结束符为止。
//返回文件的代码行行数 int CodeLineCount(char *filename){ FILE * fp; char c; int code=0, codeline=0, lineCount=0; fp=fopen(filename,"r"); c=fgetc(fp); while(c!=EOF){ if(isalnum(c)){ if(code==1){ //判断是否已有一个有效字符 codeline=1; //标记为代码行的开始 code=0; }else code=1; }else if(c=='\n'&&codeline==1){ lineCount++; code=0; codeline=0; }else if(c=='/'){ //排除注释行 c=fgetc(fp); if(c=='/'){ c=fgetc(fp); while(c!='\n') c=fgetc(fp); }else if(c=='*'){ c=fgetc(fp); while(c!='/') c=fgetc(fp); c=fgetc(fp); } } c=fgetc(fp); } if(c==EOF&&codeline==1) //为了防止最后一行没有换行而没读到换行符导致没有计数 lineCount++; return lineCount; }
9、CommentLineCount()
思路:此函数返回文件的注释行行数,包括单行注释与多行注释。首先使用fopen()打开文件,使用fgetc()来读取文件中的数据,一个一个字符读入,当读入字符为’/’时,继续读入字符。若为’/’,则为单行注释,一直到换行符,计数+1,继续读入下一字符;若为’*’,则为多行注释,先计数+1(因为注释结束标志前是没有换行符的),在结束之前循环读入下一字符,遇到换行符则计数+1,直到循环结束。继续读入下一字符并判断,不断循环,一直到读入文件结束符为止。
//返回文件的注释行行数 int CommentLineCount(char *filename){ FILE * fp; char c; int lineCount=0; fp=fopen(filename,"r"); c=fgetc(fp); while(c!=EOF){ if(c=='/'){ //继续判断是否为注释行 c=fgetc(fp); if(c=='/'){ //单行注释 lineCount++; c=fgetc(fp); while(c!='\n') c=fgetc(fp); }else if(c=='*'){ //多行注释 lineCount++; c=fgetc(fp); while(c!='/'){ if(c=='\n'){ lineCount++; c=fgetc(fp); }else c=fgetc(fp); } } } c=fgetc(fp); } return lineCount; }
六、测试运行
1、空文件
2、只有一个字符的文件
3、只有一个词的文件
Word.c
4、只有一行的文件
Line.c
5、一个典型的源文件
Source.c
七、PSP——实际耗时
PSP2.1 |
Personal Software Process Stages |
实际耗时(分钟) |
Planning |
计划 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
Development |
开发 |
750 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
· Design Spec |
· 生成设计文档 |
20 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
10 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
· Design |
· 具体设计 |
30 |
· Coding |
· 具体编码 |
540 |
· Code Review |
· 代码复审 |
20 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
60 |
Reporting |
报告 |
60 |
· Test Report |
· 测试报告 |
40 |
· Size Measurement |
· 计算工作量 |
10 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 |
合计 |
|
840 |
八、项目小结
1、对于c语言不够熟练,所以写的时候不能很流畅的写出来,总是需要翻书查找;
2、逻辑虽然正确,但性能可能并不算好;
3、有时候逻辑不够严谨,导致无法继续运算;
4、再次加深了对c语言的运用;
5、了解了项目开发的过程,学习了更多。