软工实践第二次作业
WordCount
Github项目地址:https://github.com/czx731039257
PSP表格
| psp2.1 | personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 90 |
| Estimate | 估计这个任务需要多少时间 | 665 | 710 |
| Development | 开发 | 60 | 70 |
| Analysis | 需求分析(包括学习新技术) | 180 | 150 |
| Design Spec | 生成设计文档 | 20 | 20 |
| Design Review | 设计复审 | 20 | 15 |
| Coding Standrd | 代码规范(为目前的开发制定合适的规范) | 15 | 10 |
| Design | 具体设计 | 30 | 25 |
| Coding | 具体编辑 | 150 | 250 |
| Code Review | 代码复审 | 30 | 30 |
| Test | 测试(自我测试,修改代码,提交修改) | 30 | 40 |
| Reporting | 报告 | 60 | 50 |
| Test Repor | 测试报告 | 20 | 15 |
| Size Measurement | 计算工作量 | 30 | 20 |
| Postmortem&Process Improvement Plan | 事后总结,并提出过程改进计划 | 20 | 15 |
| - | 合计 | 665 | 710 |
解题思路描述
首先拿到题目,我就先上网查了关于C++怎么实现文件的输入和输出,还有关于怎么进行接口封装的知识。再初步掌握了这些操作后,我就开始设计每个模块要猜用的结构和数据结构。首先是第一部分统计字符,我直接从get()函数每次从文本中读出一个字符。
第二部分的统计单词数,显然需要用到多个循环结构,由于这部分单词是有要求,所以这部分的是整个代码的关键之处,直接影响到后面功能,在这部分需要仔细的考虑好每个限制条件,反复的进行测试,方可开展下一个模块的工作。
最后的统计词频模块,我想的是利用哈希表来完成。再考虑好每个模块的需求后,我需要查找好自己需要的相关资料,然后再去动手打代码,这样会比较方便。
设计实现过程
这里由于需要用到哈希表,所以我先创建好链表指针。首先,我用ifstream创建文件流对象,然后用 文件流对象.open 的格式打开文件进行读取,读取时用get()函数每次读取一个字符到程序中。
在读完并统计好字符数后,我利用了多个循环结构来实现单词的判断和提取,并将每个单词散列到哈希表中为最后的统计词频做准备。
其次统计行数,我先按顺序遍历文本,当遇到有若可使干个字符(换行符和制表符)和一个换行符就把行数加一。
最后的统计词频,我利用了单词的前3个字母进行散列(字母1x26+字母2x26x26+字母3x26x26x26)这样把全部单词散列到结构体指针数组去。然后开始输出最多的10个单词,在这里我首相想到的是利用排序来解决。
改进思路
在打完代码后,由于考虑到不知道测试文本是否很大,如果很大的话,那么我用排序的方法必然会花费很多时间在这部分上,想遍历10遍的数组,每次找到频次最多的单词,输出单词和次数,然后在哈希表中删除这个单词,这样来减少遇到大数据量的运行时间。
代码说明
关键代码一
int Text::countword(ofstream *outfile)//统计单词数
{
int flag = 0;
string temp = "";
int len = all.length();//计算文本的长度
for (int i = 0; i < len; i++)
{
if ((all[i] >= 65 && all[i] <= 90) || (allg[i] >= 97 && all[i] <= 122))//找到第一个字母
{
flag = 0;
for (int j = i; j <= i + 3; j++)//判断是不是匹配单词
{
if (all[j] <= 64 || (all[j] >= 91 && all[j] <= 96) || all[j] >= 123 || len - i < 4)
{
flag = 1;
break;
}
}
if (flag == 0)//如果是匹配单词就提取单词到temp
{
temp = "";
for (; i < len && ((all[i] >= 65 && all[i] <= 90) || (all[i] >= 97 && all[i] <= 122) || (all[i] >= 48 && all[i] <= 57)); i++)
{
if (all[i] >= 65 && all[i] <= 90)//当遇到大写字母时变为小写
all[i] += 32;
temp += all[i];
}
count_word++;
//cout << temp << endl;
Text::insert(temp);//把每个单词插入哈希表中
}
else//如果不是匹配单词就跳到下一个单词的第一个字母
{
for (; (all[i] >= 65 && all[i] <= 90) || (all[i] >= 97 && all[i] <= 122||(all[i] >= 48 && all[i] <= 57)); i++) {}
}
}
else if (all[i] >= 48 && all[i] <= 57)//如果单词是以数字开头的话就跳过这个单词
{
for (; (all[i] >= 65 && all[i] <= 90) || (all[i] >= 97 && all[i] <= 122) || (all[i] >= 48 && all[i] <= 57); i++) {}
}
}
*outfile << "words:" << count_word << endl;//输出单词数
return count_word;
}
关键代码二
void Text::insert(string w)//把单词插入哈希表
{
int hash = ((w[0] - 96)) + ((w[1] - 96) * 26) + ((w[2] - 96) * 26 * 26);//计算哈希值
node *p = new node("", 1);
node *q = new node("", 1);
if (hash_table[hash]->next == NULL)//空表插入
{
p = hash_table[hash];
hash_table[hash] = new node(w, 1);
hash_table[hash]->next = p;
}
else//非空表
{
int flag = 0;
q = p = hash_table[hash];
while (p->next != NULL)//遍历链表
{
if (p->word == w)//在表中找到该单词,并且times加1
{
p->times++;
flag = 1;
}
q = p;
p = p->next;
}
if (flag == 0)//在链表中没有找到该单词,则在链表尾部插入新结点
{
node *newnode = new node(w, 1);
q->next = newnode;
newnode->next = p;
}
}
return;
}
void Text::rank(ofstream *outfile)//统计词频
{
int num;
int flag = 0;//判断出现次数最大的结点是不是表首 0不是 1是
node *max, *q, *p, *front_max;
front_max = new node("", 0);
for (int j = 0; j < 10 && j < count_word; j++)//遍历10次哈希表
{
max = new node("", 0);//初始化max
for (int i = 0; i <= 18279; i++)
{
if (hash_table[i]->next == NULL) continue;//空表跳过
else//遍历非空表
{
q = p = hash_table[i];
while (p->next != NULL)
{
if (p->times > max->times || (p->times == max->times&&p->word < max->word))
{
if (p == hash_table[i])
{
flag = 1;//表示该单词在表头
num = i;
}
else flag = 0;//表示该单词在表中
max = p;
front_max = q;
}
q = p;
p = p->next;
}
}
}
if (max->times != 0)//如果max被替换过,则说明哈希表中有单词
{
cout << "<" << max->word << ">:" << max->times << endl;//输出一个结果
*outfile << "<" << max->word << ">:" << max->times << endl;//输出一个结果
}
else //如果max没有被替换,则此时哈希表是空的,不需要输出
break;
if (flag == 1) hash_table[num] = max->next;//如果频次最大的单词在表首,替换表首指针
else front_max->next = max->next;//如果频次最大的单词在链表中,删除结点
}
return;
}
实践心得
从这次实践作业中学到了很多东西,比如说知道了怎么接口封装,怎么从文本中读入数据,其实很多的东西自己上网看别人的代码一看就懂,可是自己动手的时候就不会了,这说明看懂了不是真的懂,关键还是要自己动手敲敲代码,这样才能加深自己的记忆。
浙公网安备 33010602011771号