一.Github项目地址:
https://github.com/maoshuo1754/WordCount
结对成员博客地址:https://blog.csdn.net/qq_38916865/article/details/86527013?tdsourcetag=s_pcqq_aiomsg
二、小组成员及分工
1120161754 毛硕
1120161747 林宇翔
我负责基础功能,林宇翔负责界面实现。
三.预估时间
PSP2.1 | Personai software process stage | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 50 |
Estimate | 估计这个任务需要多长时间 | 30 | 30 |
Development | 开发 | 120 | 120 |
Analysis | 需求分析(包括学习新技术) | 60 | 80 |
Design Spec | 生成设计文档 | 80 | 100 |
Design review | 设计复审 | 60 | 60 |
Coding Standard | 代码规范 | 30 | 50 |
Design | 具体设计 | 60 | 90 |
Coding | 具体编码 | 240 | 300 |
Code review | 代码复审 | 120 | 200 |
Test | 测试(自我测试,修改代码,提交修改) | 360 | 400 |
Reporting | 报告 | 300 | 300 |
Test Report | 测试报告 | 120 | 100 |
Size Measurment | 计算工作量 | 20 | 30 |
Postmortem&Process Inprovement Plan | 事后总结,并提供过程改进计划 | 60 | 60 |
合计 | 1690 | 2000 |
四、解题思路描述
4.1 参数功能及要求
参数 | 功能 |
-x | 显示图形界面 |
-s | 递归处理目录下符合条件的文件 |
-c | 输出字符数 |
-w | 输出词的数目 |
-l | 输出行数 |
-a | 输出更复杂的信息(空行,代码行,注释行) |
4.2 解题思路
这个题目比较简单,字符数就是统计所有不是 '/n' 和空格的字符;单词数,以字母之前和之后均不为字母为一个单词;行数就是统计 '/n' 的个数。
对于注释行,有两种情况,一种是 "//",另一种是 "/*" 和 "*/" 组合的,这两种情况要分开考虑,需要注意的情况比较多。
对于空行,就是一行的可显示字符<=1就统计为一个空行。
对于代码行,总行数-注释行-空行 就是代码行。
五、设计实现过程
5.1 项目流程图
5.2 函数组成
5.2.1 统计注释行
int count_noteline(char file[])//查注释行
{
int n_num = 0;
int line = 0;
int ch_num = 0;//表示注释字符数<=1则说明本行为注释行
int flag_1 = 0;//表示前一个字符是‘/’
int flag_2 = 0;//表示是/*形式的 多行注释
int flag_3 = 0;//配合flag_2统计多行注释
int thisline = 0;//说明本行是注释行,直接跳入下一行
char ch;
FILE* fp1;
if (fopen_s(&fp1, file, "r") != 0)//失败
{
cout<<"打开文件失败"<<endl;
return -1;
}
while ((ch = fgetc(fp1)) != EOF)
{
if (ch == '\n')
{
if (line && ch_num > 0)
{
line++;
}
ch_num = 0;
flag_1 = thisline = 0;
}
if (thisline == 1)//说明本行已经是注释,跳入下一行
{
continue;
}
if (ch != ' '&&ch != '\t'&&ch != '\n')
{
ch_num++;
}
if (flag_2)
{
if (ch != ' '&&ch != '\t'&&ch != '\n')
{
ch_num++;
}
if (ch == '*')
{
flag_3 = 1;
}
else if (ch == '/'&&flag_3)
{
n_num += line;
line = 0;
flag_2 = flag_3 = 0;
thisline = 1;
}
else flag_3 = 0;
}
else if (ch == '/')
{
if (flag_1 == 0)
{
flag_1 = 1;
}
else if (flag_1 == 1 && ch_num <= 3)
{
n_num++;
thisline = 1;
}
}
else if (ch == '*')
{
if (flag_1 == 1)
{
flag_2 = 1;
line = 1;
}
}
else
{
flag_1 = 0;
}
}
fclose(stdin);
return n_num;
}
5.2.2 递归处理
通过函数GetModuleFileName(NULL, szPath, MAX_PATH)得到当前.exe文件所在路径,然后删除文件名,可以得到当前文件目录,然后通过这个目录,利用search_file函数进行文件查找。
search_file函数:
void search_file(string path, int idx)
{
struct _finddata_t filefind;
cout << path << endl;
string cur = path + "*.*";//查找全部文件
int done = 0;
int handle;
if ((handle = _findfirst(cur.c_str(), &filefind)) != -1)
{
while (!(done = _findnext(handle, &filefind)))
{
if (strcmp(filefind.name, "..") == 0)
{
continue;
}
if ((_A_SUBDIR == filefind.attrib))//判断当前文件是否是一个文件夹
{
cur = path + filefind.name + '\\';//进入文件夹
search_file(cur, idx); //递归处理
}
else //查找当前文件夹里所有的符合结尾的文件
{
int len = strlen(filefind.name);
for (int i = 0; i < len; i++)
{
if (filefind.name[i] == '.')
{
len = i;
break;
}
}
if (strcmp(filefind.name + len, para[idx] + 1) == 0) //文件尾一样的话就是符合了,比如都是.cpp结尾
{
cur = path + filefind.name;
printf("%s:\n", filefind.name);
for (int i = 1; i < idx; i++)
{
command(para[i], &cur[0]);
}
}
}
}
_findclose(handle);
}
}
5.2.3 -x图形界面
界面最终结果如下
5.3 测试
5.3.1 普通命令
5.3.2递归(对于每个文件,先输出路径 再输出文件名,然后执行相应的命令)
5.3.3 路径错误
5.3.4 某些命令未识别
六、性能
性能占比最大的都是系统函数,说明算法本身的复杂度并不高,能很快完成。
七、心得与收获
本次结对项目因为中间有考试,所以历时比较长,相比于个人项目来说,结对项目的难点,一是整个软件的实现更为复杂,代码量更大;二是如何与队友相互协作,在面对一个问题时能够达成一致。
这次的项目,极大地提升了我们我做项目的能力和与队友的协作能力,收获很大。