WHCSRL 技术网

C++学习之第六天

二、编程题--统计圣经出现的单词以及词频。

1、统计一篇英文(The_Holy_Bible.txt)文章中出现的单词和词频,输入:某篇文章的绝对路径输出:词典(词典中的内容为每一行都是一个“单词 词频”)

词典的存储格式如下:

  1. | a 66 |
  2. | abandon 77 |
  3. | public 88 |
  4. | ...... |
  5. |_________________|
  6. struct Record
  7. {
  8. string _word;
  9. int _frequency;
  10. };
  11. class Dictionary
  12. {
  13. public:
  14. //......
  15. void read(const std::string &filename);
  16. void store(const std::string &filename);
  17. private:
  18. vector<Record> _dict;
  19. };

提示:因为我们需要统计圣经文件中单词以及该单词在文件中出现的次数,所以可以看去读圣经文件,然后将单词存到数据结构中,并记录单词的次数,如果单词第二次出现的时候,只需要修改单词的次数(也就是这里说的单词的频率),这样当统计完整个圣经文件后,数据都存在数据结构vector了。接着遍历vector数据结构就可以将单词以及单词次数(也就是频率)存储到另外一个文件。(当然如果不存到另外一个文件,就只能打印到终端了)

注意:在读圣经文件的时候,有可能字符串是不合法的,比如:abc123 abc?这样的字符串,处理方式两种:直接不统计这样的字符串或者将非法字母去掉即可。

最终得到结果类似:

a 10

public 20

welcome 30.......

main.cpp

主函数部分:

        1.读取圣经数据,统计单词词频,存入vector

        2.对vector进行排序

        3.遍历或者把结果直接存入文件

  1. #include "Bible.h"
  2. void test01()
  3. {
  4. Dictionary s1;
  5. s1.read("bible.txt"); //1.读取圣经数据,统计单词词频,存入vector
  6. s1.dict_Sort(); //2.对vector中的单词进行排序
  7. //s1.foreach_vector();//3.遍历词典vector,把结果输出到控制台
  8. s1.store("my_dict.txt");//4.把结果存入文件
  9. }
  10. int main()
  11. {
  12. test01();
  13. system("pause");
  14. return EXIT_SUCCESS;
  15. }

Bible.h

        1.头文件部分

                1.定义一个Record结构体,存储单词以及其词频

                2.定义一个类,类里有个字典vector,用来存储Record

                3.类里主要需要完成的函数:

                        a.read(file):读取数据,进行数据处理,把结果存入dict vector中

                                1.ifstream 从文件中读取数据

                                2.ifstream的用法,设置循环读取,每次读取一个单词

                                3.对读出来的单词进行处理:大写转小写、标点符号转空格

                                4.继续处理 步骤3转换过的单词,去掉单词的首尾空格

                                5.单词去掉首尾空格后,如果中间还有空格,该单词不合法

                                6.判断单词是否合法,如果合法,把单词存入结构体

                                7.判断单词是否在vector中出现,出现,对应词频加1,没出现,push_back.

                                

                        b.dict_Sort();用algorithm中的sort对vector进行排序,自己写排序规则

                        c.遍历vector,把结果输出到控制台

                        d.把vector中的数据对齐,并且写入到文件,ofstream

Bible.h

  1. #pragma once
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include <iostream>
  4. using namespace std;
  5. #include<string>
  6. #include <vector>
  7. #include <sstream>
  8. #include <fstream>
  9. #include <algorithm>
  10. #include <iomanip>
  11. struct Record
  12. {
  13. string _word;
  14. int _frequency;
  15. };
  16. class Dictionary
  17. {
  18. public:
  19. void read(const string &filename);//1.读取圣经,进行字符统计,把结果并且存入_dict vector
  20. void dict_Sort();//排序
  21. void foreach_vector();//遍历vector
  22. void store(const string &filename);//2.从vector中把结果写入新文件
  23. private:
  24. vector <Record> _dict;
  25. string int2String(int number);//int类型转为字符串流,ostringstream
  26. void trim(string &s);//string字符串去掉首尾空格
  27. int compare(vector <Record> &vec, string &word);//判断_dict中是否有该单词,如果有,返回该单词的下标,没有返回-1
  28. bool judgefullword(string &word);//判断单词是否正确
  29. };

bible.cpp

  1. #include "Bible.h"
  2. void Dictionary::read(const string &filename)//读取数据并且进行处理
  3. {
  4. ifstream ifs(filename);//1.ifstream 从文件中读取数据
  5. if (!ifs.good())//文件不存在就退出
  6. {
  7. cerr << "ifstream is not good" << endl;
  8. return;
  9. }
  10. string word;
  11. while (ifs >> word)//2.默认以空格为分隔符,每次读一个单词
  12. {
  13. //istringstream ss(word); //把word变成string流
  14. //string key; //用来接收string
  15. //ss >> key; //key接收读出来的字符串
  16. //把key中的字符,大写变成小写
  17. for (int i = 0; i < word.size(); i++)//3.把单词的大写转成小写,非字母类型转为空格
  18. {
  19. if (word[i] >= 'A' && word[i] <= 'Z')
  20. {
  21. word[i] += 32;
  22. }
  23. if (word[i] >= 'a' &&word[i] <= 'z')
  24. {
  25. continue;
  26. }
  27. word[i] = ' '; //标点符号变成空格
  28. }
  29. trim(word); //4.去掉word的首尾空格,如果word全是空字符, 会把word置空,word.empty()会为真
  30. bool flag_no_error = judgefullword(word);//5.去掉首尾空格后,单词中间还有其他不合法字符,就跳过这个单词
  31. if (!word.empty()&&flag_no_error==true)//6.单词不为空,并且为合法单词
  32. {
  33. if (_dict.empty())//7.如果_dict为空,直接把Record插入,不需要比较
  34. {
  35. struct Record r = { word, 1 };
  36. _dict.push_back(r);
  37. }
  38. else
  39. {
  40. int ret = compare(_dict, word);//8.如果_dict不空,先判断_dict中是否有该单词,有的话,对应词频加1
  41. if (ret != -1)
  42. {
  43. _dict[ret]._frequency++;//ret是word在_dict中的位置,词频加1
  44. }
  45. else
  46. {
  47. struct Record r = { word, 1 };//_dict中没有这个单词,把word插入到vector
  48. _dict.push_back(r);
  49. }
  50. }
  51. }
  52. }
  53. }
  54. //string字符串去掉首尾空格
  55. void Dictionary::trim(string &s)
  56. {
  57. if (!s.empty())
  58. {
  59. s.erase(0, s.find_first_not_of(" "));
  60. s.erase(s.find_last_not_of(" ") + 1);
  61. }
  62. }
  63. //判断_dict中是否有该单词,如果有,返回该单词的下标,没有返回-1
  64. int Dictionary::compare(vector <Record> &vec, string &word)
  65. {
  66. for (int i = 0; i < vec.size(); i++)
  67. {
  68. if (vec[i]._word == word)
  69. {
  70. return i;
  71. }
  72. }
  73. return -1;
  74. }
  75. //判断读出来的单词是否正确
  76. bool Dictionary::judgefullword(string &word)
  77. {
  78. for (int i = 0; i < word.size(); i++)
  79. {
  80. if (word[i]<'a' || word[i]>'z')
  81. return false;
  82. }
  83. return true;
  84. }
  85. //遍历_dict,对齐输出到控制台
  86. void Dictionary::foreach_vector()
  87. {
  88. for (int i = 0; i < _dict.size(); i++)
  89. {
  90. cout << int2String(i + 1) << " "
  91. << setiosflags(ios::left) << setw(32) << _dict[i]._word << " "
  92. << _dict[i]._frequency << endl;
  93. }
  94. }
  95. //把int转成string
  96. string Dictionary::int2String(int number)
  97. {
  98. ostringstream oss;
  99. oss << number; //把number写入oss流
  100. return oss.str();//返回oss字符串
  101. }
  102. //用于dict_Sort,按照单词的大小来排序
  103. bool string_compare(struct Record &p1, struct Record &p2)
  104. {
  105. return p1._word < p2._word;
  106. }
  107. void Dictionary::dict_Sort()
  108. {
  109. sort(_dict.begin(), _dict.end(), string_compare);
  110. }
  111. //把统计数据输出到文件
  112. void Dictionary::store(const string &filename)
  113. {
  114. ofstream ofs(filename);//ofstream,把结果数据输出到文件
  115. if (!ofs.good())
  116. {
  117. cerr << "ofstream is not good " << endl;
  118. return;
  119. }
  120. #if 0
  121. for (int i = 0; i < _dict.size(); i++)
  122. {
  123. ofs <<int2String(i+1) <<" "<<_dict[i]._word << " " << _dict[i]._frequency << endl;
  124. }
  125. #endif
  126. ostringstream my_ss;
  127. for (int i = 0; i < _dict.size(); i++)
  128. {
  129. //1.把数据先对齐写入字符串流
  130. my_ss << int2String(i + 1) << " " //单词序号,把int转成string
  131. <<setiosflags(ios::left)<<setw(32)<< _dict[i]._word<<" "//单词对齐
  132. << _dict[i]._frequency << endl;//词频
  133. }
  134. //cout << my_ss.str() << endl;
  135. //2.再把数据写入到文件
  136. ofs << my_ss.str() << endl;
  137. ofs.close();
  138. }

推荐阅读