需求分析:
程序处理用户需求的模式为:
- wc.exe [parameter][filename]
在[parameter]中,用户通过输入参数与程序交互,需实现的功能如下:
1、基本功能
- 支持 -c 统计文件字符数
- 支持 -w 统计文件单词数
- 支持 -l 统计文件总行数
2、拓展功能
- 支持 -a 返回高级选项(代码行 空行 注释行)
[filename] 是待处理文件名。
能力有限,没有写出来-s:递归处理目录下符合条件的文件的功能,高级功能-x。
基本功能的实现:
这次写的程序条理还比较清晰,实现四个功能写了四个子,函数其中void CharCount(FILE *fp),用于统计文件中字符的数量,void WordCount(FILE *fp),用于统计文件中单词的数量,void LineCount(FILE *fp),用于统计文件的行数,void Advance(FILE *fp),实现统计文件中的代码行,注释行,空行的数量。
利用main函数的参数传递输入内容,使用命令行程序cmd调试参数。由于没想到怎么来控制不确定参数个数的情况,只能机械的自己排序,然后判断输入情况进行程序调试与运行。之前以为自己不会用文件,现在感觉挺简单的嘛。定义文件指针保存文件地址。然后根据输入参数个数的情况进行不同情况的函数调用,偷懒了,没有把参数大于三个时的情况都写下来。肯定有宝藏待我发掘。在主函数中获得参数内容,根绝参数内容进入不同的子函数输出不同的内容。主函数代码如下:
1 int main(int argc, char* argv[]) 2 { 3 FILE *fp; 4 switch(argc) 5 { 6 //不同的输入情况 7 case 3: 8 fp=fopen(argv[2],"r"); 9 if(fp==NULL) 10 { 11 printf("不能打开文件"); 12 exit(0); 13 } 14 if(strcmp(argv[1],"-c")==0) 15 { 16 CharCount(fp); 17 } 18 else if(strcmp(argv[1],"-w")==0) 19 { 20 WordCount(fp); 21 } 22 else if(strcmp(argv[1],"-l")==0) 23 { 24 LineCount(fp); 25 } 26 else if(strcmp(argv[1],"-s")==0) 27 { 28 Advance(fp); 29 } 30 else 31 { 32 printf("格式错误!\n"); 33 } 34 break; 35 case 4: 36 fp=fopen(argv[3],"r"); 37 if(fp==NULL) 38 { 39 printf("不能打开文件"); 40 exit(0); 41 } 42 if(strcmp(argv[1],"-c")==0) 43 { 44 if(strcmp(argv[2],"-w")==0) 45 { 46 CharCount(fp); 47 WordCount(fp); 48 } 49 else if(strcmp(argv[2],"-l")==0) 50 { 51 CharCount(fp); 52 LineCount(fp); 53 } 54 else 55 { 56 printf("格式错误!\n"); 57 } 58 } 59 else if(strcmp(argv[1],"-w")==0&& 60 strcmp(argv[2],"-l")==0) 61 { 62 WordCount(fp); 63 LineCount(fp); 64 } 65 else 66 { 67 printf("格式错误!\n"); 68 } 69 break; 70 case 5: 71 fp=fopen(argv[4],"r"); 72 if(fp==NULL) 73 { 74 printf("不能打开文件"); 75 exit(0); 76 } 77 if((strcmp(argv[1],"-c")==0)&& 78 (strcmp(argv[2],"-w")==0)&& 79 (strcmp(argv[3],"-l")==0)) 80 { 81 CharCount(fp); 82 WordCount(fp); 83 LineCount(fp); 84 } 85 else 86 { 87 printf("格式错误!\n"); 88 } 89 break; 90 case 6: 91 fp=fopen(argv[5],"r"); 92 if(fp==NULL) 93 { 94 printf("不能打开文件"); 95 exit(0); 96 } 97 if((strcmp(argv[1],"-c")==0)&& 98 (strcmp(argv[2],"-w")==0)&& 99 (strcmp(argv[3],"-l")==0)&& 100 (strcmp(argv[4],"-s")==0)) 101 { 102 CharCount(fp); 103 WordCount(fp); 104 LineCount(fp); 105 Advance(fp); 106 } 107 else 108 { 109 printf("格式错误!\n"); 110 } 111 break; 112 default: 113 { 114 printf("参数个数不正确!\n"); 115 exit(0); 116 } 117 } 118 return 0; 119 }
测试文件如下,第一行没有字符直接回车,第二行三个空格一个tab行缩进,主要用来测试空行:
当捕捉到用户的输入的指令含有-c时,调用统计文件中字符的数量的函数,每读取一个字符,字符数量增加一,最后输出总字符的数量。最后把文件的指针重新指向文件流的开头,避免其他函数读文件时出错。代码和程序运行截图如下:
1 void CharCount(FILE *fp) 2 { 3 int charcount=0; 4 char ch; 5 ch=fgetc(fp); 6 //使用feof函数判断文件是否结束,feof函数适合于各种文件 7 //读取一个字符,字符数增1 8 while(!feof(fp)) 9 { 10 charcount++; 11 ch=fgetc(fp); 12 } 13 14 printf("字符数:%d\n",charcount); 15 //文件指针指向流的开头 16 rewind(fp); 17 }
当捕捉到用户输入的指令含有-w时,调用统计文件中单词的数量的函数,我把单词定义为字母数字或者下划线开头并且由字母数字或下划线构成的字符串,所以每当第一个字符属于字母数字或者下划线的话这个字符串至少有一个单词了,然后继续往下读字符,直到下个字符不是字母数字或者下划线,然后单词数量增一。单个数字也会被当成字符串,可以把第一个字符的判断改成字母或者下划线就行了,判断是不是字母可以用isalpha函数。isalnum函数用来判断是不是字母或者数字。下划线的ASCII码为95,也可以直接写:'_'。代码和程序运行截图截图如下:
1 void WordCount(FILE *fp) 2 { 3 int wordcount=0; 4 char ch; 5 ch=fgetc(fp); 6 while(!feof(fp)) 7 { 8 //字母数字或者下划线开头 9 if(isalnum(ch)||ch==95) 10 { 11 //而且后续字符也是字母数字或者下划线 12 while(isalnum(ch)||ch==95) 13 { 14 ch=fgetc(fp); 15 } 16 //单词数量增加1 17 wordcount++; 18 } 19 20 ch=fgetc(fp); 21 } 22 printf("单词数:%d\n",wordcount); 23 rewind(fp); 24 }
当捕捉到用户输入的指令含有-l时,调用统计文件的行数的函数,每遇到’\n‘或者’\0‘时,行数加一。代码和程序运行截图如下:
void LineCount(FILE *fp) { int linecount=0; char ch; ch=fgetc(fp); //遇到换行符或者文件结尾行数增1 while(!feof(fp)) { if(ch=='\n'||ch=='\0') { linecount++; } ch=fgetc(fp); } printf("行数:%d\n",linecount+1); rewind(fp); }
基本功能指令统一输入的截图如下:
部分扩展功能的实现:
void Advance(FILE *fp),实现统计文件中的代码行,注释行,空行的数量。
判断规则:空行:本行全为空行或是格式控制字符,若包含代码,则只有不超过一个可显示字符,如 "{";注释行:含有“//”的行,如果是代码+“//”,也把这一行当做注释行;代码行:除去空行和注释行的其他行。
因为统计的单位是行,所以这次是一次读一行的内容,方便统计。然后根据每行的内容对不同变量进行增一的操作。代码和程序运行的截图如下:
void Advance(FILE *fp) { //代码行 int CodeLine=0; //空行 int EmptyLine=0; //注释行 int AnnotaLine=0; int i=0; int linecount=0; char ch,str[100]; //获取总行数 ch=fgetc(fp); while(!feof(fp)) { if(ch=='\n'||ch=='\0') { linecount++; } ch=fgetc(fp); } rewind(fp); while(!feof(fp)) { //读取一行字符串来判断每行状态 fgets(str,sizeof(str),fp); //空行 if(str[0]=='\n') { EmptyLine++; continue; } //空行 for(i=0;str[i]!='\n';i++) { //本行代码含有大括号 if(str[i]=='{'||str[i]=='}') { //大括号后面没有代码,空行增1,读取下一行 if(str[i+1]=='\n') { EmptyLine++; goto ABC; } else { //大括号后面是空或者tab,继续判断下一个字符 while(str[i+1]==' '||str[i+1]=='\t') { i++; //满足循环条件且最后是换行符,空行增1 if(str[i+1]=='\n') { EmptyLine++; goto ABC; } } } } else //本行代码只有空行或者tab,空行增1 while(str[i]==' '||str[i]=='\t') { i++; if(str[i]=='\n') { EmptyLine++; goto ABC; } } } //注释行加1 for(i=0;str[i]!='\n'&&str[i]!='\0';i++) { //判断第一个字符是不是注释字符 if(str[i]=='/') { //判断第二个字符,是的话注释行增1 if(str[i+1]=='/') { AnnotaLine++; break; } //不是的话直接跳出循环,减少时间耗费 else break; } } ABC:; } printf("空行:%d\n",EmptyLine); printf("注释行:%d\n",AnnotaLine); //总行数减去空行和注释行得到代码行数 printf("代码行:%d\n",linecount-EmptyLine-AnnotaLine+1); }
代码基本功能和部分高级功能已经全部实现,附上综合到一块的程序运行的截图:
总结:程序的基本功能实现的还是比较顺利的,但是高级功能的实现很艰难,只是空行一项内容我就花费了好几个小时去思考,真的想了,但就是没有思路。现在即便写出来了也还是有很多bug。底层人民的悲哀那,看着人家快准狠的写完程序,自己努力奋斗只是个残次品,不受打击是不可能的。但是也很无奈。