软件工程实践之词频统计
Github:https://github.com/1561602610/PersonProject-C2
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 720 | 1000 |
Development | 开发 | 600 | 900 |
• Analysis | • 需求分析 (包括学习新技术) | 180 | 210 |
• Design Spec | • 生成设计文档 | 40 | 50 |
• Design Review | • 设计复审 | 30 | 60 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 30 |
• Design | • 具体设计 | 30 | 50 |
• Coding | • 具体编码 | 180 | 260 |
• Code Review | • 代码复审 | 30 | 40 |
• Test | • 测试(自我测试,修改代码,提交修改) | 120 | 200 |
Reporting | 报告 | 120 | 100 |
• Test Repor | • 测试报告 | 30 | 20 |
• Size Measurement | • 计算工作量 | 30 | 30 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 50 |
合计 | 720 | 1000 |
需求分析
-
统计文件的字符数:
- 只需要统计Ascii码,汉字不需考虑
- 空格,水平制表符,换行符,均算字符
-
统计文件的单词总数,单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写。
- 英文字母: A-Z,a-z
- 字母数字符号:A-Z, a-z,0-9
- 分割符:空格,非字母数字符号
- 例:file123是一个单词, 123file不是一个单词。file,File和FILE是同一个单词
- 统计文件的有效行数:任何包含非空白字符的行,都需要统计。
- 统计文件中各单词的出现次数,最终只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
计算模块接口的设计与实现过程
实现字符计数:
#include "CharNum.h" #include<fstream> #include<iostream> int CharNum(char * filename) { int count = 0; char ch; FILE *file; fopen_s(&file,filename, "rt"); for (; (ch=fgetc(file)) != EOF;) { if (ch >= 0&&ch <= 255) count++; } fclose(file); return count; }
实现单词计数:
#include"WordNum.h" int WordNum(char * filename) { map<string, int> Word_Num_map; char ch; FILE *file; fopen_s(&file, filename, "rt"); int flag = 0; int count = 0; for (; (ch = fgetc(file)) != EOF;) { if ((ch >= 97 &&ch <= 122 )|| (ch >= 65 && ch <= 90))//英文字母 { if (flag >= 0)flag++; if (flag < 0)flag--; } else if (ch >= 48 && ch <= 57)//数字 { if (flag >= 4)flag++; else flag = -1; } else //非字母数字符号 { if (flag >= 4) { count++; flag = 0; } else { flag = 0; } } } fclose(file); return count; }
实现行数计数:
#include "LineNum.h" int LineNum(char * filename) { FILE *file; fopen_s(&file,filename, "rt"); int count = 0; char ch; int flag = 0; for (; (ch = fgetc(file)) != EOF;) { if (ch == '\n') { if (flag > 0)count++; flag = 0; } else if (ch != ' '&&ch!='\t') { flag++; } }if (flag > 0)count++; fclose(file); return count; }
实现词频统计及输出前十名:
#include"Word_Fre.h" typedef pair<string, double> PAIR; bool CmpByValue(const PAIR& lhs, const PAIR& rhs) { return (lhs.second != rhs.second) ? lhs.second > rhs.second : lhs.first < rhs.first; } int Word_Fre(char * filename) { map<string, int> Word_Num_map; char ch; string word; int flag = 0; FILE *file; fopen_s(&file, filename, "r"); for (; (ch = fgetc(file)) != EOF;) { if ('A' <= ch && ch <= 'Z') ch = ch + 32; if (ch >= 'a' && ch <= 'z')//英文字母 { if (flag >= 0) { flag++; word = word + ch; } if (flag < 0) { flag = 0; word = ""; } } else if (ch >= 48 && ch <= 57)//数字 { if (flag >= 4) { flag++; word = word + ch; } else { flag = 0; word = ""; } } else //非字母数字符号 { if (flag >= 4) { Word_Num_map[word]++; word = ""; flag = 0; } else { flag = 0; word = ""; } } } if (flag >= 4) { Word_Num_map[word]++; } vector <PAIR> Word_Num_vec(Word_Num_map.begin(), Word_Num_map.end()); sort(Word_Num_vec.begin(), Word_Num_vec.end(), CmpByValue); if (Word_Num_vec.size() < 10) for (int i = 0; i != Word_Num_vec.size(); ++i) { const char *ss = Word_Num_vec[i].first.c_str(); cout << '<' << ss << '>' << ":" <<' '<< Word_Num_vec[i].second << endl; } else for (int i = 0; i != 10; ++i) { const char *ss = Word_Num_vec[i].first.c_str(); cout << '<' << ss << '>' << ":" <<' '<< Word_Num_vec[i].second << endl; } return 0; }
主要解题思路:
设置标志位flag用于判断该位是否为单词的组成部分。
测试样例:
测试文本:
测试结果:
心路历程与收获:
在努力完成这次实践的过程中,遇到了很多问题,首先的问题就是关于文件读取的问题,在遇到一个个问题的时候四处查找资料、请教同学,感觉确实有在学到东西。
在解决词频统计这个问题上花了很长的时间,主要是用于思考记录单词及其频率的方法,后来查找以及问同学相关问题,知道了map这个东东还有一堆奇奇怪怪的函数,实在是后悔没好好学C++和数据结构。