写在最前:
参考17079张武科同学的代码,故博文中形如详细代码设计等部分引用了张武科的博文,具体引用部分不详细标出,其他部分仍为本人原创
github地址
https://github.com/mrlandiao/wc
PSP表格
PSP2.1 | PSP阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 25 | 45 |
· Estimate | · 估计这个任务需要多少时间 | 25 | 35 |
Development | 开发 | 700 | 1000 |
· Analysis | · 需求分析 (包括学习新技术) | 150 | 200 |
· Design Spec | · 生成设计文档 | 10 | 20 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 35 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 20 |
· Design | · 具体设计 | 60 | 70 |
· Coding | · 具体编码 | 600 | 700 |
· Code Review | · 代码复审 | 100 | 150 |
· Test | · 测试(自我测试,修改代码,提交修改) | 200 | 200 |
Reporting | 报告 | 80 | 100 |
· Test Report | · 测试报告 | 30 | 40 |
· Size Measurement | · 计算工作量 | 10 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 15 | 25 |
| 合计 | 2045 | 2660 |
解题思路
参考上学期编写的词法分析和语法分析程序的基本功能,对其进行修改,得到main函数。利用控制台执行exe文件,通过传参的不同来实现所要求的功能
程序设计实现过程
本项目采用C语言实现,除main函数外,还有多个自定义函数,其中两个最主要的函数为count()和none(),count()用来统计单词数、行数、注释行数,none()用来统计空行数。
main()函数根据传入的形参的不同,完成不同的输出。其余自定义行数比较简单,下面代码说明中会以isNewline()函数为例进行解释。
代码说明
count()函数用以统计单词数、行数、注释行数,函数中有单词判断,没检测出一个单词word_num+1;每遇到一个'\n',line_num+1;没有到注释行,note_num+1,如果是/*note*/注释,在主是内部没出现一次‘\n’,note_num+1。
int count()//统计单词数、行数、注释行数
{
get();
clearToken();
while (isSpace(ch) || isNewline(ch) || isTab(ch) || isComma(ch))
{
if (isNewline(ch)) line_num++;
get();
}
if (isStar(ch))
{
catToken(ch);
}
else if (isDivi(ch))
{
catToken(ch);
get();
if (isStar(ch))
{
note_num++;
catToken(ch);
do
{
do
{
get();
if (isNewline(ch))
{
note_num++;
line_num++;
}
} while (!isStar(ch));
do
{
get();
if (isNewline(ch))
{
note_num++;
line_num++;
}
if (isDivi(ch))
{
catToken(ch);
return 0;
}
} while (isStar(ch));
} while (!isStar(ch));
}
if (isDivi(ch))
{
catToken(ch);
do
{
get();
} while (!isNewline(ch));
note_num++;
}
if (isNewline(ch))
line_num++;
}
else if (isEnd(ch))
return 0;
else
{
while (!(isSpace(ch) || isNewline(ch) || isTab(ch) || isComma(ch) || isDivi(ch)))
{
catToken(ch);
get();
}
retract();
if(strcmp(stopWord, "") == 0 || strcmp(token,stopWord)!=0)
word_num++;
}
return 0;
}
以isNewline()为例,解释简单自定义函数:
bool isNewline(char a)
{
if (a == '\n')
return true;
return false;
}
none()函数,根据两个‘\n’中是否小于或等于一个字符来判断是否为空行,满足条件则none_num+1:
void none()//统计空行数
{
int i,j = 0;
for (i = 0;; i++)
{
if (art[i] == EOF)
break;
if (art[i] != '\n'&& art[i] != '\t'&& art[i] != ' ')
{
j++;
}
if (art[i] == '\n')
{
if (j <= 1)
none_num++;
j = 0;
}
}
}
main()函数,由于功能是逐步添加的,加上自己的代码习惯有待提高,导致main()函数被修改的过于冗杂。重要思想:利用数组flag[7],每一个元素对应一个功能(包括字符数、单词数输出等),为1时表示对应功能执行,反之不执行。
int main(char argc, char* argv[])
{
if (argc<3)
printf("请在控制台输入正确命令!\n");
else
{
if (strcmp(argv[0], "wc.exe") != 0)
printf("可执行文件名输入错误!\n");
else
{
for (int i = 1; i<argc; i++)
{
if (strcmp(argv[i], "-c") == 0)
{
flag[0] = 1;
}
else if (strcmp(argv[i], "-w") == 0)
{
flag[1] = 1;
}
else if (strcmp(argv[i], "-l") == 0)
{
flag[2] = 1;
}
else if (strcmp(argv[i], "-o") == 0)
{
flag[3] = 1;
}
else if (strcmp(argv[i], "-s") == 0)
{
flag[4] = 1;
}
else if (strcmp(argv[i], "-a") == 0)
{
flag[5] = 1;
}
else if (strcmp(argv[i], "-e") == 0)
{
flag[6] = 1;
}
}
}
}
if (flag[4] == 1)
{
_finddata_t sFind;
long lResult = 0;
lResult = _findfirst("*.c", &sFind);
if (lResult == -1) {
printf("没有找到文件。");
return 0;
}
int i=0,len;
do {
len = strlen(sFind.name);
strcpy_s(filesName[i], len + 1, sFind.name);//将所有读到的保存到filesname
file_num++;
i++;
} while (_findnext(lResult, &sFind) != -1);
for (int j = 0; j < file_num; j++)
{
fopen_s(&fp, filesName[j], "r");
if (fp == NULL)
printf("error!\n");
int i = 0;
do//获取文件内容
{
ch = fgetc(fp);
art[i++] = ch;
} while (ch != EOF);
fclose(fp);
char_num = i;
if (flag[6] == 1)//获取停用词
{
fopen_s(&fp, "stopList.txt", "a");
fgets(stopWord, 20, fp);
fclose(fp);
}
do//核心统计
{
count();
} while (!isEnd(ch));
none();
if (flag[0] == 1)
printf("%s,字符数: %d \n", filesName[j], char_num);
if (flag[1] == 1)
printf("%s,单词数: %d \n", filesName[j], word_num);
if (flag[2] == 1)
printf("%s,总行数: %d \n", filesName[j], line_num);
if (flag[3] == 1)
{
fopen_s(&fp, argv[argc - 1], "a");
if (flag[0] == 1)
fprintf(fp, "%s,字符数: %d \n", filesName[j], char_num);
if (flag[1] == 1)
fprintf(fp,"%s,单词数: %d \n", filesName[j], word_num);
if (flag[2] == 1)
fprintf(fp,"%s,总行数: %d \n", filesName[j], line_num);
if (flag[5] == 1)
fprintf(fp, "%s,代码行/空行/注释行: %d/%d/%d \n", filesName[j], line_num - note_num - none_num, none_num, note_num);
fclose(fp);
}
if (flag[5] == 1)
printf("%s,代码行/空行/注释行: %d/%d/%d \n", filesName[j], line_num - note_num - none_num, none_num, note_num);
strcpy_s(stopWord,1,"");
for (int i = 0; i < 1000; i++)
art[i] = '\0';
sta = art;
char_num = 0;
word_num = 0;
line_num = 0;
note_num = 0;
none_num = 0;
}
}
else
{
fopen_s(&fp, "file.c", "r");
if (fp == NULL)
printf("error!\n");
int i = 0;
do
{
ch = fgetc(fp);
art[i++] = ch;
} while (ch != EOF);
fclose(fp);
char_num = i;
if (flag[6] == 1)
{
fopen_s(&fp, "stopList.txt", "r");
fgets(stopWord, 20, fp);
fclose(fp);
}
do
{
count();
} while (!isEnd(ch));
none();
if (flag[0] == 1)
printf("file.c,字符数: %d \n", char_num);
if (flag[1] == 1)
printf("file.c,单词数: %d \n", word_num);
if (flag[2] == 1)
printf("file.c,总行数: %d \n", line_num);
if (flag[3] == 1)
{
fopen_s(&fp, argv[argc - 1], "w");
if (flag[0] == 1)
fprintf(fp, "file.c,字符数: %d \n", char_num);
if (flag[1] == 1)
fprintf(fp, "file.c,单词数: %d \n", word_num);
if (flag[2] == 1)
fprintf(fp, "file.c,总行数: %d \n", line_num);
if (flag[5] == 1)
fprintf(fp, "file.c,代码行/空行/注释行: %d/%d/%d \n", line_num - note_num - none_num, none_num, note_num);
fclose(fp);
}
if (flag[5] == 1)
printf("file.c,代码行/空行/注释行: %d/%d/%d \n", line_num - note_num - none_num, none_num, note_num);
}
printf("\n");
system("pause");
return 0;
}
测试设计过程
利用“wc.bat”脚本程序,批量处理,测试用例如下:
测试结果为
测试数据集是我自己随意输入的,详情参见同目录下file.c文件,测试输出结果我已经检查,确认无误。需要说明的是其中仅包含 “ { ” 的行算作空行,不算作代码行。
另外,当“-s”和“-o”命令同时输入时,写入文件之前不会清空文件;仅包含“-o”命令而不包含“-s”命令时,写入文件之前会清空文件。