一.Github项目地址:https://github.com/changjiang666/wordCount
二.PSP表格
WC问题统计C源文件中的字符,单词,行数,输出到默认文件或者指定文件中。拓展功能是实现多文件处理,文本行分类,忽略特定单词。根据需求,PSP表格如下:
PSP2.1 |
PSP阶段 |
预估耗时 (分钟) |
实际耗时 (分钟) |
Planning |
计划 |
5 | 10 |
· Estimate |
· 估计这个任务需要多少时间 |
5 | 10 |
Development |
开发 |
515 | 860 |
· Analysis |
· 需求分析 (包括学习新技术) |
20 | 30 |
· Design Spec |
· 生成设计文档 |
10 | 20 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
5 | 10 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 | 30 |
· Design |
· 具体设计 |
10 | 10 |
· Coding |
· 具体编码 |
400 | 600 |
· Code Review |
· 代码复审 |
20 | 100 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
30 | 60 |
Reporting |
报告 |
100 | 100 |
· Test Report |
· 测试报告 |
20 | 20 |
· Size Measurement |
· 计算工作量 |
20 | 30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
20 | 10 |
合计 |
620 | 970 |
三.解题思路
WC问题在UNIX系统的内核得以实现,骨干代码在黑皮C里面看到过,大概20行左右。刚拿到题目就没怎么在意,后来发现拓展功能也比较麻烦。整个题目的难点在于字符串的处理,文件操作不麻烦,只需要读操作就行,至于写操作只需要输出重定行即可。另一个难点在于递归处理当前路径下来的所有文件,刚开始想直接利用C进行系统调用,借助操作系统来完成这个功能(后来发现系统自动解析,白写了比较麻烦的系统调用),然而没法递归处理,GG。至于找资料,主要就是看了一下黑皮C最后面的库函数,需要用的时候在MSDN上面直接查。
1.UNIX系统中wc程序的骨干代码
#include <stdio.h>
#define IN 1
#define OUT 0
int main(int argc, char const *argv[])
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == ' ')
++nl;
if (c == ' ' || c == ' ' || c == ' ')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d ", nl, nw, nc);
return 0;
}
基本功能实现就是抄的这个代码,只是把字符来源改成了缓存。
2. C的文件读操作
static int
filelength(FILE *fp);
char *
readfile(char *path);
这个也是网上的,基本照抄。
3. 输出重定向就是一行代码,利用了redirection。
四.程序设计实现过程
根据功能划分,写了3个源文件。
file.c主要是文件操作,wc.c是整个程序的核心部分,help.c是给用户提供帮助的,用户输入-help可以查看帮助和自动打开作业网页。
1.file,h
Readfile把文件读到缓存,processBuf从缓存中提取出停用词表,返回指向这些字符的指针的指针。为了防止程序恶意修改缓存,将其设置为const。
getDir是递归处理所有的文件,没有用。
2. wc.h
先定义了一个struct,存储统计的基本信息,其实不用也行,用了只是为了方便,减少自己编程时需要记忆的内容。
CWL是统计文本信息的,利用了CPU运行程序的思想,把所有的东西都统计出来,需要什么就输出什么,缺点是增加了运行时间,不过只是增加了常系数。
在wc.c定义了一个外部属性限制在本文件范围的函数。
3.help.h
只有一个Help方法,输出帮助信息。
这几个函数基本没有关系,分开设计就是为了高内聚低耦合,全部的调用由main函数执行。
五.代码说明
介绍2个比较麻烦的部分:
1. main函数对命令行参数的解析
#if !DEBUG
if(1 == argc)
{
printf("Please Open In CMD! At CMD, Enter 'wc.exe -help' For More Help! ");
system("pause");
return EXIT_FAILURE;
}
#endif
for(i = 1; i < argc; ++i)
{
p = argv[i];
/*capture -s*/
if(strcmp(p, "-s") == 0)
s_state = TRUE;
/*capture -a*/
if(strcmp(p, "-a") == 0)
a_state = TRUE;
if(strcmp(p, "-c") == 0)
c_state = TRUE;
if(strcmp(p, "-w") == 0)
w_state = TRUE;
if(strcmp(p, "-l") == 0)
l_state = TRUE;
if(strcmp(p, "-help") ==0)
{
help();
return EXIT_SUCCESS;
}
/*loop until p point to ' '*/
while(*++p != NULL)
;
/*p go back 2 char and compare to ".c"
in order to identify the path name*/
if(strcmp(p - 2, ".c") == 0)
path[j++] = argv[i];
/*p go back 4 char and compare to ".txt", at the same time argv[i - 1]
stores "-e", so we can identify the word_list path and check user valid input*/
if(strcmp(p - 4, ".txt") == 0 && strcmp(argv[i - 1],"-e") == 0 )
{
word_list_path = argv[i];
}
/*p go back 4 char and compare to ".txt", at the same time argv[i + 1]
stores null pointer, so we can identify the file name*/
if(strcmp(p - 4, ".txt") == 0 && argv[i + 1] == ' ')
{
/*if lack of "-o" before file name, throw error information*/
if(strcmp(argv[i - 1], "-o") == 0)
filename = argv[i];
else
{
if(strcmp(argv[i - 1], "-e") != 0)
{
printf("command line error. ");
return EXIT_FAILURE;
}
}
}
}
利用指针处理读取到的字符,由于要处理可能的各种输入,所以有些if嵌套比较深。到了最后感觉利用开关语句可以好一些,不想修改了,时间不够。在处理读取的每个命令行参数时,其实就是对状态变量的修改过程。如果处理一个-s, 就将s_state设置为TRUE,利用状态变量来决定最后的输出。对于处理技巧,注释基本都说了,不再赘述。
2.利用DFA处理代码行/注释行/空行
确定的有限自动机解题有一步是状态化简,去掉无效状态和多余状态,在整个过程中设置了5个状态,PRECODE, CODE, PRECOMMENT, COMMENT, SPACELINE,对于/* */ 多行注释的问题,最后利用状态变量influence进行纠正。时间问题,这段代码写的很丑,最后也没有优化,所有就只贴了部分进行说明。
最后利用处理结果对相应的统计变量进行自增运算。
六.测试设计过程
1. 路径测试
main函数中if路径比较多,将每一条路径进行测试。
2.边界值测试
在测试文件中敲入各种可能的注释和空行
3.等价类划分
同类情况只测试一次。
#0help功能测试
start wc.exe wc.exe -help
#1基本功能测试
start wc.exe wc.exe -c -w -l test.c
#2基本功能测试---输出到指定的文件
start wc.exe wc.exe -c -w -l test.c -o output1.txt
#3检测用户的恶意输入(想输出全部结果但是没有输入-s)
start wc.exe wc.exe -c *.c -o output2.txt
#4检测用户的恶意输入(没有输入待检测的.c文件)
start wc.exe wc.exe -w -c -l
#5当前路径全部文件是否读取成功测试
start wc.exe wc.exe -s -c -w -l *.c -o output3.txt
#6检测用户输入错误的文件名
start wc.exe wc.exe -s -c -w -l finally.c -o output4.txt
#7复杂数据类型检测
start wc.exe wc.exe -s -a -c -w -l *.c -o output5.txt
#8停用词表检测
start wc.exe wc.exe -s -c -w -l *.c -o output6.txt -e stoplist.txt
#9检测使用停用词表命令出错
start wc.exe wc.exe -s -c -w -l *.c -o output7.txt stoplist.txt
#10检测输出到指定文件命令出错
start wc.exe wc.exe -s -c -w -l *.c output8.txt
#11全部功能集成测试
start wc.exe wc.exe -s -a -c -w -l *.c -o output9.txt -e stoplist.txt
七. 参考网址
1.https://msdn.microsoft.com/zh-cn/library/ftsafwz3.aspx
2.http://www.cnblogs.com/berthua/p/7654892.html