本次编程项目的主题为英文单词词频统计,主要分为下面四个小项目:
1、统计已存在的文档中的英文单词词频统计,并降序输出;
2、用户输入文件名,程序对其中的内容进行词频统计并降序输出;
3、用户输入指定文件夹的路径,程序遍历路径下的所有文件,并分别对其中的内容进行词频统计,降序输出前10个单词的词频;
4、用户在控制台按照要求输入文本内容,并将输入的内容存入指定文本中,程序对输入的内容进行词频统计并降序输出。
我在本科学期间进行过C/C++的学习,所以本次作业使用的语言为C++,使用codeblock进行代码编写;但是由于时间距离较长,遗忘了很多,在编程开始前用了一些时间进行复习。因为编程序的时候将四个小项目放在四个文件中,后来为了方便使用才将它们的代码放在了一个文件中,并进行用户输入选择,实现不同的项目功能,所以不同选择中会有代码重复,这次由于时间问题没有进行解决,之后会将其进行合并解决。项目选择部分如下图:
图 1 项目选择头部
下面对每个小项目进行详细介绍:
项目一:
项目一的功能是统计已存在的文档(test2.txt)中的英文单词词频统计,并降序输出。我构建了名为wq的结构体,创建结构体数组word[M]来存放单词的内容和词频;将大写字母均换成了相应的小写字母,避免单词因大小写不同而重复;在单词读取过程中我进行了是否读到文件末尾的判断;使用稳定的排序算法——冒泡排序按照词频数进行降序排列,最终输出。项目一的重点和难点主要是实现文档读取的功能。项目一的运行截图如下:
图 2 项目一运行截图
重要代码片段如下(打开文件和读取并存储单词信息):
fp = fopen("C://Users//Administrator//wf//test2.txt", "r");//读取文件,若有需求则应改为你要读取的文件的绝对路径 //读所有单词 while (!feof(fp)){ //feof()检测是否达到文件尾部 ch = getc(fp);//获取当前字符 if (ch == ‘ ‘ || ch == 10){ //ASCLL码中10为换行符,此条件为遇到空格或换行则跳过 continue; } if ((ch >= ‘a‘&&ch <= ‘z‘) || (ch >= ‘A‘&&ch <= ‘Z‘)) { //发现一个单词 K = 0; t_word.count = 1; while ((ch >= ‘a‘&&ch <= ‘z‘) || (ch >= ‘A‘&&ch <= ‘Z‘) || (ch == ‘\‘‘)){ if (ch >= ‘A‘&&ch <= ‘Z‘) ch += 32; //大写转换成小写 t_word.danci[K++] = ch; //把当前字母存入数组 ch = getc(fp); } t_word.danci[K++] = ‘\0‘;//结束标志\0,一个单词结束 j = n; //j用来记录当前单词的个数 for (i = 0; i<j; i++) { //与前面的单词比较 if (strcmp(t_word.danci, word[i].danci) == 0){ //如果有单词相同 word[i].count++;//则此类单词加1 break; } } if (n == 0 || i == j){ //前面如果没有相同的单词 word[n] = t_word;//则为此类单词开辟新单元 n++; } } }
项目二:
项目二的功能是用户输入文件名,程序对其中的内容进行词频统计并降序输出。项目二实际上是在项目一的基础上增加了文件名输入和读取相对路径下名称特定的文件的功能,这也是此项目的一个重点、难点。项目二的运行截图如下:
图 3 项目二运行截图
由于单词数量较多,当运行结束后在控制台界面上无法出现前面的单词等内容,所以我将项目二的运行情况进行了分别截图,并在程序末尾又增加了显示不重复单词总数的输出语句,以证明统计不重复单词总数这个功能的能够正确使用。
重要的代码片段如下(文件名输入和读取路径):
int K, n = 0, i, j; string name; string lujing; cout<<"请输入文件名 Jane_Eyre"<<endl; cin>>name; lujing="C://Users//Administrator//wf//folder//"+name+".txt"; string s=lujing; char *dst = new char[255]; for(i=0; i <=s.length(); i++) dst[i]=s[i]; dst[i] = ‘\0‘; fp=fopen(dst,"r");
项目三:
项目三功能是用户输入指定文件夹的路径,程序遍历路径下的所有文件,并分别对其中的内容进行词频统计并降序输出前10个单词的词频。项目三特有的重点是遍历指定目录下的所有文件,并依次对文件进行读取操作,我使用listFiles()方法进行目录文件的查询。由于我使用了_findfirst()、_findnext()进行搜索,得到了“.”和“..”两个文件夹名,经过上网查找资料发现“”这两个可以忽略。此外,项目三中获取输入地址时我使用的是cin.getline,但是在运行程序时发现没有进行输入,程序就跳出了,也是上网查询后加入了“cin.ignore();”语句得到了解决。项目代码中我定义当输入‘\’符号表示地址路径输入结束,点击回车键即可进行下面的查询、统计工作。在指定目录下我存放了三个不同名称和内容的TXT文档。项目要求分别输出每个文档词频数最高的10个单词及其词频数,只需将循环输出的上界进行修改。项目三的运行截图如下:
图 4 项目三运行截图
重要代码片段如下(查找路径下的所有文件):
void listFiles(const char * dir){ intptr_t handle; _finddata_t findData; handle = _findfirst(dir, &findData); // 查找目录中的第一个文件 if (handle == -1){ cout << "无法找到目录中的第一个文件!\n"; return; } while (_findnext(handle, &findData) == 0){ if (findData.attrib & _A_SUBDIR&& strcmp(findData.name, ".") == 0&& strcmp(findData.name, "..") == 0) // 是否是子目录并且不为"."或".." cout<<endl; else{ txtname[f]=findData.name; f++; } } _findclose(handle); // 关闭搜索句柄 }
项目四:
项目四的功能是用户在控制台按照要求输入文本内容,并将输入的内容存入指定文本中,程序对输入的内容进行词频统计并降序输出。项目四我使用了fstream和io.h头文件中的函数进行文件的读写操作,若当前路径下没有指定文件则先创建新文件,再进行读写操作。我对写入操作设置了特定的结束符“EOF”,输入结束后按下空格键,再输入“EOF”就表示文字输入完毕。项目四的运行截图如下:
重要代码片段如下(打开文件并输入文本内容):
fstream iofile("test.txt",ios::out); if(!iofile){ cout << "创建新文档中……\n"; exit(0); } cout << "请输入内容(输入结束后请按回车并输入“EOF”结束标志):"<<endl; while(1){ getline(cin,str); if(str=="EOF") break; iofile << str << endl; } iofile.close();
PSP阶段表格
分类 |
预计花费时间(分) |
实际花费时间(分) |
项目一 |
120 |
61 |
测试项目一并完善修改 |
80 |
|
项目二 |
180 |
52 |
测试项目二并完善修改 |
164 |
|
项目三 |
240 |
95 |
测试项目三并完善修改 |
246 |
|
项目四 |
200 |
98 |
测试项目四并完善修改 |
112 |
|
项目最终测试和修改 |
120 |
124 |
对比我计划和实际消耗的时间发现,虽然在编程之前对程序的实现过程和算法有了一定的了解,但实际消耗的时间要远远大于计划时间,尤其在测试和修改项目时花费了很多时间。我觉得这很大的原因是不能熟练地掌握C++的一些函数和算法,在程序运行时遇到了很多问题,从而需要特别多的时间去查询相应的原因和解决方法,这样在项目实现部分就花费了很多超计划的时间。
代码及版本控制
地址:https://git.coding.net/jyj5951/wf.git