wordcount
github项目地址:https://github.com/hellostronger/personalWork
具体项目要求
WordCount
- 实现一个简单而完整的软件工具(源代码特征统计程序)
- 进行单元测试、回归测试、效能测试,在实现上诉程序的过程中使用相关工具
- 进行个人软件过程(psp)的实践,逐步记录自己在每个软件工程花费的时间
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 返回更复杂的数据(代码行 / 空行 / 注释行)。
空行:本行全部是空格或格式控制字符,如果包括代码,则只有不超过一个可显示的字符,例如“{”。
代码行:本行包括多于一个字符的代码。
注释行:本行不是代码行,并且本行包括注释。一个有趣的例子是有些程序员会在单字符后面加注释:
} //注释
在这种情况下,这一行属于注释行。
高级功能:
-x 参数。这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。
需求举例:
wc.exe -s -a *.c
返回当前目录及子目录中所有*.c 文件的代码行数、空行数、注释行数。
困难描述
-
对文件操作陌生
通过看书理一遍文件相关操作,最后得到了解决,因为c的文件操作课堂上略微带过,所以一直是弱项,通过这次项目,巩固了一遍c的文件操作。 -
对命令行操作陌生
一开始看到命令行输入感到恐惧,后来才发现其实跟普通函数一样,只不过在main传进参数,进而,我理解了Linux平常的一些终端操作的背后也是普通的一个个函数,对我学习终端操作产生了帮助。 -
编写可视化界面不懂如何下手
当第一次接触项目要求的时候,看见可视化界面心里面想到的是java,但看到了项目举例中的.exe后缀,心里面想到了c,故最后选择了c,埋下了伏笔,最后安排两天时间进行可视化界面要求的完成,通过摸索后来发现c编写可视化界面门槛高,最终几番尝试,失败。 -
文件操作时遇到的问题
测试的时候把文件放在桌面目录下,发现进不了第二层,后来发现桌面下文件的图标和在D盘目录下的图标不一样,因为我的代码中使用
if(data.attrib == _A_SUBDIR)
我觉得应该是桌面和D盘的文件夹类别不一致,后来在D盘处测试了,而在处理路径名的时候也因为字符串问题摸索很久
- 文件的模糊查询
此处应该使用正则表达式,但是c标准库不支持正则,代码用了取巧办法解决*.c的问题,只支持显示所有.c文件 - 代码操作
因为刚开始测试的时候在.txt文件没问题,后来拿.c文件测试发现结果不一样,debug很久,最后做了一些特殊操作把不合理情况消除
部分代码说明
- main(int i,char *argv[])通过指针数组接收命令行输入的参数,要写参数不符的情况,因为要通过编译,生成.exe文件,将打印放在了主函数中,让子函数只负责计算,比较取巧的是 代码行=总行-注释行-空行
int main(int i,char *argv[]){
if(i!=3&&i!=2)
printf("命令错误,格式wc.exe [parameter] [file_name]\n您可输入wc.exe -h 查看相关参数和帮助");
if(i == 2&&strcmp("-h",argv[1])==0){
printf(" -w 返回文件词的数目\n -c 返回文件字符数\n -l 返回文件的行数 \n -s 递归处理目录下符合条件的.c文件\n -a 返回文件详细信息");
}
else if(i == 3){
if(strcmp("-c",argv[1])==0){
printf("%s文件中的字符数为%d\n",argv[2], getnum_char(argv[2]));
}
if(strcmp("-w",argv[1])==0)
{
printf("%s文件中单词数为%d\n",argv[2],getnum_word(argv[2]));
}
if(strcmp("-l",argv[1])==0){
printf("%s文件中的行数为%d\n",argv[2],getnum_row(argv[2]));
}
if(strcmp("-a",argv[1])==0){
int total,zs,kh,dm;
total = getnum_row(argv[2]);
zs = getnum_zhushirow(argv[2]);
kh = getnum_null(argv[2]);
dm = total - zs -kh;
printf("%s文件中的总行为%d\n",argv[2],total);
printf("文件中的注释行为%d\n",zs);
printf("文件中的空行为%d\n",kh) ;
printf("文件中的代码行为%d\n",dm) ;
}
if(strcmp("-s",argv[1])==0){
int i = 0;
char a[30] = {};
while(argv[2][i]!='.'&&argv[2][i+1] != 'c'){
a[i] = argv[2][i];
i++;
}
search(a);
}
return 0;
}
}
- 只支持*.c情况,能够递归查询子目录下文件
void search(char *path){
struct _finddata_t data;
long hnd = _findfirst(path,&data);
if(hnd < 0 ){
perror(path);
}
int nRet = (hnd<0)? -1:1;
while(nRet >=0){
char *nextpath = NULL;
if(data.name[0]=='.'){
nRet = _findnext(hnd,&data);
continue;
}
if(data.attrib == _A_SUBDIR){
nextpath = (char *) malloc ((strlen(path)+strlen(data.name))*sizeof(char));
char *path2 = (char *) malloc (strlen(path)*sizeof(char));
for(int i=0;i<strlen(path)-1;i++)
path2[i] = path[i];
sprintf(nextpath,"%s%s\\*",path2,data.name);
free(path2);
search(nextpath);
}else{
for (int i=0;i<strlen(data.name);i++){
if(data.name[i]=='.'&&data.name[i+1]=='c'){
char *path3 = (char *) malloc((strlen(path))*sizeof(char));
for(int i=0;i<strlen(path)-1;i++)
{
path3[i] = path[i];
}
char *filepath = (char *) malloc ((strlen(path3)+strlen(data.name)+2)*sizeof(char));
sprintf(filepath,"%s%s",path3,data.name);
allinformation(filepath);
free(filepath);
free(path3);
}
}
}
free(nextpath);
nRet = _findnext(hnd,&data);
}
_findclose(hnd);
}
- 其它代码
void allinformation(char f[]){//文件的更多注释
int total,zs,kh,dm;
printf("%s文件中的字符数为%d\n",f, getnum_char(f));
printf("文件中单词数为%d\n",getnum_word(f));
printf("文件中的行数为%d\n",getnum_row(f));
total = getnum_row(f);
zs = getnum_zhushirow(f);
kh = getnum_null(f);
dm = total - zs -kh;
printf("文件中的总行为%d\n",total);
printf("文件中的注释行为%d\n",zs);
printf("文件中的空行为%d\n",kh) ;
printf("文件中的代码行为%d\n",dm) ;
}
void search(char *path){//找规定目录下.c文件
struct _finddata_t data;
long hnd = _findfirst(path,&data);
if(hnd < 0 ){
perror(path);
}
int nRet = (hnd<0)? -1:1;
while(nRet >=0){
char *nextpath = NULL;
if(data.name[0]=='.'){
nRet = _findnext(hnd,&data);
continue;
}
if(data.attrib == _A_SUBDIR){//是目录
nextpath = (char *) malloc ((strlen(path)+strlen(data.name))*sizeof(char));
char *path2 = (char *) malloc (strlen(path)*sizeof(char));
for(int i=0;i<strlen(path)-1;i++)
path2[i] = path[i];
sprintf(nextpath,"%s%s\\*",path2,data.name);
free(path2);
search(nextpath);
}else{//是文件
for (int i=0;i<strlen(data.name);i++){
if(data.name[i]=='.'&&data.name[i+1]=='c'){
char *path3 = (char *) malloc((strlen(path))*sizeof(char));
for(int i=0;i<strlen(path)-1;i++)
{
path3[i] = path[i];
}
char *filepath = (char *) malloc ((strlen(path3)+strlen(data.name)+2)*sizeof(char));
sprintf(filepath,"%s%s",path3,data.name);//拼接.c文件的路径
allinformation(filepath);
free(filepath);
free(path3);
}
}
}
free(nextpath);
nRet = _findnext(hnd,&data);
}
_findclose(hnd);
}
int getnum_char(char f[]){//求显示字符数
FILE *fp;
char ch;
int num = 0;
fp = fopen(f,"r");
if(fp==NULL){
printf("文件读取失败");
return -1;
}
while((ch=getc(fp))!=EOF){
if(!isspace(ch)){//把空白符去掉
num++;
}
}
fclose(fp);
return num;
}
int getnum_row(char f[]){//总行
FILE *fp;
int num = 0;
fp = fopen(f,"r");
if(fp==NULL){
printf("文件读取失败");
return -1;
}
if(getnum_char(f)<=0){
fclose(fp);
return 0;
}else{
char a[100]={};
while(!feof(fp)){
fgets(a,100,fp);
num++;
}
fclose(fp);
return num-1;
}
}
int getnum_word(char f[]){//求词数
FILE *fp;
bool flag = true;
int ch;
int num = 0;
fp = fopen(f,"r");
while((ch=getc(fp))!=EOF){
if((ch>='a'&&ch<='z')||(ch<='Z'&&ch>='A')||(ch>='0'&&ch<='9'||ch=='_')){
if(flag==true){
num++;
flag = false;
}
}else{
flag = true;
}
}
fclose(fp);
return num;
}
int getnum_zhushirow(char f[]){//求注释行
bool tag = true;
bool j = false;
bool end_tag = false;
FILE *fp;
int ch,dh;
bool flag = false;
bool duanzhushi = false;
bool hangzhushi = false;
int num = 0;
fp = fopen(f,"r");
while((ch=getc(fp))!=EOF){
if(ch=='/'&&(!duanzhushi||!hangzhushi)){
flag = true;//读入第一个/
}
if(flag&&j){ //读入第二行
if(ch == '*'){ //多段注释
duanzhushi = true;
}
if(ch == '/'){//单行注释
hangzhushi = true;
}
}
if(duanzhushi){
if(ch =='\n'){
num++;
printf("%d\n",num);
}
}
if(hangzhushi){
if(ch =='\n'){
num++;
hangzhushi=false;
flag = false;
j = false;
}
}
if(ch == '*'&&duanzhushi){
end_tag = true;
}
if(ch == '/'&&end_tag){
duanzhushi = false;
end_tag = false;
flag = false;
j = false;
}
if (flag == true)
j = true;
}
if(hangzhushi)//最后一行注释
num++;
dh = delet(f);
fclose(fp);
return num-dh;
return num;
}
int getnum_null(char f[]){//求空行
int num=0;
FILE *fp;
if(fp==NULL){
printf("文件读取失败");
return -1;
}
fp = fopen(f,"r");
char a[100];
if(getnum_char(f)>0){
while(!feof(fp)){
fgets(a,100,fp);
if(a[0]=='\n'||(a[0]=='}'&&a[2]!='/')||(a[0]=='{'&&strlen(a)==2)){//空行情况
num++;
}
}
if(strlen(a)==2&&a[0]=='}')//degbug发现最后没注释时候重复输出,最后作此操作
num--;
}
fclose(fp);
return num;
}
int delet(char f[]){
int num=0;
FILE *fp;
if(fp==NULL){
printf("文件读取失败");
return -1;
}
fp = fopen(f,"r");
char a[100];
if(getnum_char(f)>0){
while(!feof(fp)){
fgets(a,100,fp);
for(int i=0;i<strlen(a)-1;i++){
if(a[i]=='/'&&a[i+1]=='/'){//即求跟在代码后面行注释
if(i>=2)
num++;
}
}
}
}
fclose(fp);
return num;
}
相关操作图片介绍
由于图片较多,收录在GitHub项目photoshop。doc中
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 15 |
| · Estimate | · 估计这个任务需要多少时间 | 20 | 15 |
| Development | 开发 | 1240 | 1800 |
| · Analysis | · 需求分析 (包括学习新技术) | 180 | 240 |
| · Design Spec | · 生成设计文档 | 10 | 0 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 0 | 0 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
| · Design | · 具体设计 | 30 | 30 |
| · Coding | · 具体编码 | 600 | 700 |
| · Code Review | · 代码复审 | 200 | 500 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 200 | 300 |
| Reporting | 报告 | 45 | 60 |
| · Test Report | · 测试报告 | 20 | 20 |
| · Size Measurement | · 计算工作量 | 5 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 30 |
| 合计 | 1305 | 1875 |
总结
这次对个人项目不够重视,该开始规划不够充分,时间安排不够合理,在debug上花费大量时间,总的来说还是基础不够扎实,但是通过这次,让我感受到紧张感,摆正了学习的态度,希望下次能够获得更大的进步。

浙公网安备 33010602011771号