c++ primer–容器的综合应用:文本查询程序

我们的程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示,即第 7 行应该在第 9 行之前输出,依此类推。
例如,以本章的内容作为文件输入,然后查找单词“element”。输出的前几行应为:
element occurs 125 times
(line 62) element with a given key.
(line 64) second element with the same key.
(line 153) element |==| operator.
(line 250) the element type.
(line 398) corresponding element.

后面省略了大约 120 行。

查询程序的设计

设计程序的一个良好习惯是首先将程序所涉及的操作列出来。明确需要提供的操作有助于建立需要的数据结构和实现这些行为。从需求出发,我们的程序需要支持如下任务
1. 它必须允许用户指明要处理的文件名字。程序将存储该文件的内容,以便输出每个单词所在的原始行。
2. 它必须将每一行分解为各个单词,并记录每个单词所在的所有行。在输出行号时,应保证以升序输出,并且不重复。
3. 对特定单词的查询将返回出现该单词的所有行的行号。
4. 输出某单词所在的行文本时,程序必须能根据给定的行号从输入文件中获取相应的行。

数据结构

我们将用一个简单的类 TextQuery 实现这个程序。再加几种容器的配合使用,就可相当巧妙地满足上述要求。

  1. 使用一个 vector 类型的对象存储整个输入文件的副本。输入文件的每一行是该 vector对象的一个元素。因而,在希望输出某一行时,只需以行号为下标获取该行所在的元素即可。
  2. 将每个单词所在的行号存储在一个 set容器对象中(一个单词可能出现多次,所以用set)。使用 set 就可确保每行只有一个条目,而且行号将自动按升序排列。
  3. 使用一个 map 容器将每个单词与一个 set 容器对象关联起来,该 set 容器对象记录此单词所在的行号。

操作

对于类还要求有良好的接口。然而,一个重要的设计策略首先要确定:查询函数需返回存储一组行号的 set 对象。这个返回类型应该如何设计呢?

查询的过程相当简单:使用下标访问 map 对象获取关联的 set 对象即可
唯一的问题是如何返回所找到的 set 对象。安全的设计方案是返回该 set 对象的副本。
如果处理的是一个相当庞大的文件,则复制 set对象的代价会非常昂贵。其他可行的方法包括:返回一个 pair 对象,存储一对指向 set 中元素的迭代器;或者返回 set 对象的 const 引用

第一、第三和第四个任务是使用这个类的程序员将执行的动作。第二个任务则是类的内部任务。将这四任务映射为类的成员函数,则类的接口需提供下列三
个 public 函数:
read_file 成员函数,其形参为一个 ifstream& 类型对象。该函数每次从文件中读入一行,并将它保存在 vector容器中。输入完毕后,read_file 将创建关联每个单词及其所在行号的map 容器。
run_query 成员函数,其形参为一个 string 类型对象,返回一个 set 对象,该 set 对象包含出现该 string 对象的所有行的行号。
text_line 成员函数,其形参为一个行号,返回输入文本中该行号对应的文本行
无论 run_query 还是 text_line 都不会修改调用此函数的对象,因此,可将这两个操作定义为 const 成员函数。

实现 read_file 功能,还需定义两个 private 函数来读取输入文本和创建 map 容器:
• store_file 函数读入文件,并将文件内容存储在 vector 容器对象中。
• build_map 函数将每一行分解为各个单词,创建 map 容器对象,同时记录每个单词出现行号。

TextQuery 类

class TextQuery { public: // typedef to make declarations easier typedef std::vector<std::string>::size_type line_no; /* interface: * read_file builds internal data structures for the given file * run_query finds the given word and returns set of lines on
which it appears * text_line returns a requested line from the input file */ void read_file(std::ifstream &is) { store_file(is); build_map(); } std::set<line_no> run_query(const std::string&) const; std::string text_line(line_no) const; private: // utility functions used by read_file void store_file(std::ifstream&); // store input file void build_map(); // associated each word with a set of line numbers // remember the whole input file std::vector<std::string> lines_of_text; // map word to set of the lines on which it occurs std::map< std::string, std::set<line_no> > word_map; };

这个类直接反映了我们的设计策略(类外调用的函数用public,类内调用的函数用private)。唯一提及的是使用 typedef 为 vector 的 size_type 定义了一个别名(行号line_no类型)
read_file 函数在类的内部定义。该函数首先调用 store_file 读取并保存输入文件,然后调用 build_map 创建关联单词与行号的 map 容器。

TextQuery 类的使用

int main(int argc, char **argv) { // open the file from which user will query words ifstream infile; if (argc < 2 || !open_file(infile, argv[1])) { cerr << "No input file!" << endl; return EXIT_FAILURE; } TextQuery tq; tq.read_file(infile); // builds query map // iterate with the user: prompt for a word to find and print results // loop indefinitely; the loop exit is inside the while while (true) { cout << "enter word to look for, or q to quit: "; string s; cin >> s; // stop if hit eof on input or a 'q'is entered if (!cin || s == "q") break; // get the set of line numbers on which this word appears set<TextQuery::line_no> locs = tq.run_query(s); // print count and all occurrences, if any print_results(locs, s, tq); } return 0; }

输出结果:print_results 函数

void print_results(const set<TextQuery::line_no>& locs, const string& sought, const TextQuery &file) { // if the word was found, then print count and all occurrences typedef set<TextQuery::line_no> line_nums; line_nums::size_type size = locs.size(); cout << "\n" << sought << " occurs " << size << " " << make_plural(size, "time", "s") << endl; //make_plural根据 size 是否为 1 输出“time”或“times”。// print each line in which the word appeared line_nums::const_iterator it = locs.begin(); for ( ; it != locs.end(); ++it) { cout << "\t(line " // don't confound user with text lines start ing at 0 << (*it) + 1 << ") " << file.text_line(*it) << endl; } }

编写成员函数

存储输入文件:store_file

第一个任务是读入需要查询的文件。使用 string 和 vector 容器提供的操作,可以很简便地实现这个任务:

// read input file: store each line as element in lines_of_text void TextQuery::store_file(ifstream &is) { string textline; while (getline(is, textline)) lines_of_text.push_back(textline); }

函数从文件中每读入一行就将它添加到名为 lines_of_text 的 vector 对象中。

建立单词 map 容器

vector 容器中的每个元素就是一行文本。要建立一个从单词关联到行号的 map 容器,必须将每行分解为各个单词。再次使用第 8.5 节描述的 istringstream(字符串输入流):

// finds whitespace-separated words in the input vector // and puts the word in word_map along with the line number void TextQuery::build_map(){ // process each line from the input vector for (line_no line_num = 0; line_num != lines_of_text.size(); ++line_num) { //we'll use line to read the text a word at a time istringstream line(lines_of_text[line_num]); string word; while (line >> word) // add this line number to the set; // subscript will add word to the map if it's not
already there word_map[word].insert(line_num); } }

or 循环以每次一行的迭代过程遍历 lines_of_text。
首先将 istringstream 对象 line 与当前行绑定起来,然后使用 istringstream 的输入操作符读入该行中的每个单词。回顾此类输入操作符与其他 istream 操作符一样,将忽略空白符号。因此,while 循环将 line 中以空白符分隔的单词读取出来。
这个函数的结尾部分类似前面的单词统计程序。将 word 用做 map 容器的下标如果 word 在 word_map 容器对象中不存在,那么下标操作符将该 word 添加到此容器中,将其关联的值初始化为空的 set。不管是否添加了 word,下标运算都返回一个 set 对象,然后调用 insert 函数在该 set对象中添加当前行号。如果某个单词在同一行中重复出现,那么 insert 函数的调用将不做任何操作

支持查询:run_query 函数

set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const { map<string, set<line_no> >::const_iterator loc = word_map.find(query_word); //在map中查找,返回指向该key值的迭代器if (loc == word_map.end()) return set<line_no>();     else// fetch and return set of line numbers for this word return loc->second;  //loc->second即是map中的set对象}

run_query 函数带有指向 const string 类型对象的引用参数,并以这个参数作为下标来访问 word_map对象。假设成功找到这个 string,那么该函数返回关联此 string 的 set 对象,否则返回一个空的 set 对象。

输出出现该单词的每一行:text_line

string TextQuery::text_line(line_no line) const { if (line < lines_of_text.size()) return lines_of_text[line]; throw std::out_of_range("line number out of range"); }

该函数带有一个行号参数,返回该行号所对应的输入文本行。应该首先检查我们要查询的行是否们于合法范围内。如果是,则返回相应的行,否则,抛出 out_of_range 异常。

c++ primer--容器的综合应用:文本查询程序相关推荐

  1. C++ 容器的综合应用的一个简单实例——文本查询程序

    [0. 需求] 最近在粗略学习<C++ Primer 4th>的容器内容,关联容器的章节末尾有个很不错的实例. 通过实现一个简单的文本查询程序,希望能够对C++的容器学习有更深的理解. 由 ...

  2. C++自学笔记_文本查询程序_《C++ Primer》

    <C++ Primer> 第10章结束,用一个文本查询程序结束本章 :) 程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词.查询的结果是该单词出现的次数,并列出每次出现所在 ...

  3. C++ primer 第12章 12.3 使用标准库:文本查询程序

    文章目录 使用标准库:文本查询程序 文本查询程序设计 数据结构 在类之间共享数据 自己的文本查询程序 书中的文本查询程序 使用标准库:文本查询程序 我们将实现一个简单的文本查询程序,作为标准库相关内容 ...

  4. 【Smart_Point】动态内存与智能指针实战:文本查询程序(设计set,map,智能指针的应用)

    文章目录 Cpp读入结构性数组 文本查询程序 文本查询程序本版1 Cpp读入结构性数组 #include<sstream> #include<iostream> #includ ...

  5. 15.9 文本查询程序再探(继承)

    本节中使用类的继承方式重写了文本查询程序,支持多种查询策略:或,与,非. 其中Query是提供给用户使用的类,含有两个接口:eval和rep,eval用于查找对应的单词出现的文本,rep用于输出用户指 ...

  6. 容器的综合应用:文本查询程序

    需求 程序读取用户指定的任意文本文件,允许用户从该文件中查找单词.查询结果是该单词出现的次数,并列出每次出现所在行,如果某单词在同一行中多次出现,程序将只显示该行一次.行号按升序显示,即第 7 行应该 ...

  7. c++ primer文本查询程序 自编加强版(c++primer5th 练习12.32-33)

    //文件 functions.cc#include <iostream> #include <string> #include <vector> #include ...

  8. c++primer 12.3.1文本查询程序

    提示:不完全类型只能在优先的情况下使用,可以定义以这个类型作为函数的参数类型或者函数返回值类型,也可以定义这个类型的指针或者引用. //文件functions.cc #include <stri ...

  9. error: passing ‘const xxx’ as ‘this’ argument discards qualifiers c++primer 5th文本查询程序一个错误请各位指教(已解决)

    文件main.cc 文件main.cc #include <string> #include <iostream> #include <memory> #inclu ...

最新文章

  1. C# MD5摘要算法、哈希算法
  2. mac上搭建vue环境及webstorm新建vue项目
  3. flowable理论(三)flowable简介
  4. 异常检测1——python使用KNN模型进行异常检测
  5. 本田2022年新车将搭载谷歌Automotive OS
  6. 建立网站需要什么条件_教育学校网站建设有什么作用?学校建立网站为的是什么?...
  7. 计算机网络国家标准,计算机网络教室建设标准本标准参考国家标准GB50174并结合金州.doc...
  8. 使用同源建模预测蛋白质结构
  9. excel表格打印每页都有表头_excel技巧:excel表格打印后每页自动带标题、页眉页脚...
  10. 国内最常用的坐标系大全
  11. 设计模式-访问者模式练习
  12. Opencv去除高光
  13. excel插入页码_当EXCEL遇上PPT 学做抢手人气王
  14. MYSQL1146解决方法
  15. 怎么用ai做出适量插画_AI怎么画矢量插画? ai手绘插画的教程
  16. Maven Scanning for projects... < org.example:MapReduceDemo > Building MapReduceDemo 1.0-SNAPSHOT BUI
  17. 【12月英语——快乐中学习】
  18. NIST Cybersecurity White Paper 2021
  19. 通向KDE4之路(三):完全的Mac OS X撑持
  20. Mixly21:遥控彩灯

热门文章

  1. 企业号、企业微信、企业邮箱三者融合,IBOS微信生态掘金之路
  2. 通过css设置canvas背景图片
  3. 架构模式 - 微内核模式
  4. YTU OJ 2458: 换啤酒
  5. Matlab画圆方向图,如何在matlab里画天线的立体方向图?
  6. [讨论] 通用(任何android机型)Root教程(完整版!附砖机自救方法)
  7. 深度学习基础知识每日更 upupup
  8. 用Excel生成频率分布表及频率分布直方图
  9. python随机森林筛选变量_用随机森林分类器和GBDT进行特征筛选
  10. min_25 JZOJ5594 最大真因数