容器的综合应用:文本查询程序
该程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。
查询的结果是该单词出现的次数,并列出每次出现的行。如果某单词在同一行多次出现,程序将只显示该行一次。行号按升序显示。
设计程序的一个良好习惯是首先将程序所涉及的操作列出来。
明确需要提供的操作有助于建立需要的数据结构和实现这些行为。
从需求出发,我们的程序需要支持如下任务:
1),它必须允许用户指明要处理的文件名字。程序将储存该文件的内容,以便输出每个但系所在的原始行。
2),它必须将每一行分解为各个单词,并记录每个单词所在的所有行。在输出行号时,应保证以升序输出,并且不重复。
3),对特定单词的查询将返回出现该单词的所有行的行号
4),输出某单词所在的行文本时,程序必须能根据给定的行号从输入文件中获取相应的行。
//所谓类,无非数据,和 函数
//程序也是如此。
1,数据结构
用一个简单的类实现这个程序,配合使用几种容器,可以满足上述要求
1),使用一个vector<string>类型的对象储存整个输入文件的副本。
输入文件的每一行是该vector对象的一个元素。因而,在希望输出某一行时,只需以行号为下标获取该行所在的元素即可。
2),将每个单词所在的行号存储在一个set容器对象中。使用set就可以确保每行只有一个条目,而且行号将自动按升序排列。
3),使用一个map容器将每个单词与一个set容器对象关联起来,该set容器对象记录此单词所在的行号。
终上所述,需要定义的类将有两个数据成员:储存输入文件的vector 对象,以及一个map容器对象,该对象关联每个输入的单词以及记录该单词所在行号的set容器对象。
2,操作
对于类还要求有良好的接口。
一个重要的设计策略首先要确定:查询函数需返回存储一组行号的set对象。这个返回类型应该如何设计?
查询的过程相当简单:使用下标访问map对象获取关联的set对象即可。唯一的问题是如何返回所找到的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容器对象,同时记录每个单词出现的行号
#include <vector> #include <map> #include <fstream> #include <set> class TextQuery { public: //typedef to make declarations easier typedef std::vector<std::string>::size_type line_no; /*interface: read_file builds internal date structures for the given file run_query finds the given word and returns set of lines on which it appears text_line returns a requeted line from the input line */ 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_line 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 occures std::map< std::string, std::set< line_no > > word_map; } ;
10.6 p325 C++primer