一个简单的文本查询程序

需求:

程序读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该程序出现的次数,并且列出程序所在的行数。如果单词在行中出现多次,则程序将只显示该行一次。行号按升序显示。

clip_image002

查询程序的设计

1. 它必须允许用户指明要处理的文件名字。程序将存储该文件的内容,以便输出每个单词所在的原始行。

2. 它必须将每一行分解为各个单词,并且记录每个单词所在的所有行。在输出行号时,保证升序输出,并且不重复。

3. 对特定单词的查询将返回出现单词的所有行的行号。

4. 输出某单词所在的行文本时,程序必须能根据给定的行号从输入文件中获取相应地行。

数据结构

设计TextQuery类实现

1. 使用一个vector<string>类型的对象存储整个输入文件的副本。输入文件的每一行是该vector对象的一个元素。因而在输出某一行时,只需以下标获取该行所在的元素即可。

2. 将每个单词所在的行号存储在一个set容器对象中。使用set就可确保每行只有一个条目,而且条目将自动升序排列。(set里面每个值都不重复)

3. 使用一个map容器将每个单词与一个set容器对象关联起来,该set容器对象记录该单词的所在的行号。

故,我们定义的TextQuery类将有两个数据成员:存储输入文件的vector对象以及一个map容器对象,改容器关联每个输入的单词和记录单词所在行的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容器对象,同时记录每个单词出现行号。

clip_image004

TextQuery类的使用

clip_image006

其中,open_file()和make_plural()很简单,这里不细讲,代码中有。

类的成员函数的实现

首先是读入需要查询的文件。使用string和vector容器提供的操作,可以很简单地实现

clip_image008

每次存储文件的一行内容将其添加到lines_of_text的vector容器中

建立单词map容器

clip_image010

for 循环以每次一行遍历lines_of_text,然后将line(istringstream对象)与当前行绑定起来,然后使用istringstream的输入操作符读入行中的每个单词,至此能够将行中单词读取出来

程序最后如果单词不存在则插入该单词,初始化set并且将该行号插入到set,由于set容器不重复故当一行同一单词多个时set只记录一个行号,如果单词已经存在,就指更新set内的行号

支持查询

clip_image012

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

run_query()返回值的使用:

运行run_query函数后,获得一组所查找的单词出现的行号。除了输出该单词的出现次数,还需要输出该单词出现的每一行

clip_image014

PS:源代码中还有个别和算法代码关系不大的函数,没做分析,这部分代码很简单,很容易看懂。

 1 #include "TextQuery.h"
 2 #include <string>
 3 #include <vector>
 4 #include <map>
 5 #include <set>
 6 #include <iostream>
 7 #include <fstream>
 8 #include <cctype>
 9 #include <cstring>
10 #include <cstdlib>
11 
12 using std::set;
13 using std::string;
14 using std::map;
15 using std::vector;
16 using std::cerr;
17 using std::cout;
18 using std::cin;
19 using std::ifstream;
20 using std::endl;
21 
22 string make_plural(size_t, const string&, const string&);
23 ifstream& open_file(ifstream&, const string&);
24 void print_results(const set<TextQuery::line_no>& locs, 
25                    const string& sought, const TextQuery &file)
26 {
27     // if the word was found, then print count and all occurrences
28     typedef set<TextQuery::line_no> line_nums; 
29     line_nums::size_type size = locs.size();
30     cout << "
" << sought << " occurs "
31          << size << " "
32          << make_plural(size, "time", "s") << endl;
33 
34     // print each line in which the word appeared
35     line_nums::const_iterator it = locs.begin();
36     for ( ; it != locs.end(); ++it) {
37         cout << "	(line "
38              // don't confound user with text lines starting at 0
39              << (*it) + 1 << ") "
40              << file.text_line(*it) << endl;
41     }
42 }
43 
44 // program takes single argument specifying the file to query
45 int main(int argc, char **argv)
46 {
47     // open the file from which user will query words
48     ifstream infile;
49     if (argc < 2 || !open_file(infile, argv[1])) {
50         cerr << "No input file!" << endl;
51         return EXIT_FAILURE;
52     }
53 
54     TextQuery tq;
55     tq.read_file(infile);  // builds query map
56 
57     // iterate with the user: prompt for a word to find and print results
58     // loop indefinitely; the loop exit is inside the while
59     while (true) {
60         cout << "enter word to look for, or q to quit: ";
61         string s;
62         cin >> s;
63 
64         // stop if hit eof on input or a 'q' is entered
65         if (!cin || s == "q") break;
66 
67         // get the set of line numbers on which this word appears
68         set<TextQuery::line_no> locs = tq.run_query(s);
69 
70         // print count and all occurrences, if any
71         print_results(locs, s, tq);
72      }
73     return 0;
74 }
 1 #ifndef TEXTQUERY_H
 2 #define TEXTQUERY_H
 3 #include <string>
 4 #include <vector>
 5 #include <map>
 6 #include <set>
 7 #include <iostream>
 8 #include <fstream>
 9 #include <cctype>
10 #include <cstring>
11 
12 class TextQuery {
13     // as before
14 public:
15     // typedef to make declarations easier
16     typedef std::string::size_type str_size;
17     typedef std::vector<std::string>::size_type line_no;
18 
19     /* interface:
20      *    read_file builds internal data structures for the given file
21      *    run_query finds the given word and returns set of lines on which it appears
22      *    text_line returns a requested line from the input file
23     */
24     void read_file(std::ifstream &is) 
25                { store_file(is); build_map(); }
26     std::set<line_no> run_query(const std::string&) const; 
27     std::string text_line(line_no) const;
28     str_size size() const { return lines_of_text.size(); }
29     void display_map();        // debugging aid: print the map
30 
31 private:
32     // utility functions used by read_file
33     void store_file(std::ifstream&); // store input file
34     void build_map(); // associated each word with a set of line numbers
35 
36     // remember the whole input file
37     std::vector<std::string> lines_of_text; 
38 
39     // map word to set of the lines on which it occurs
40     std::map< std::string, std::set<line_no> > word_map;  
41     // characters that constitute whitespace
42     static std::string whitespace_chars;     
43     // canonicalizes text: removes punctuation and makes everything lower case
44     static std::string cleanup_str(const std::string&);
45 };
46 #endif
  1 #include "TextQuery.h"
  2 #include <sstream>
  3 #include <string>
  4 #include <vector>
  5 #include <map>
  6 #include <set>
  7 #include <iostream>
  8 #include <fstream>
  9 #include <cctype>
 10 #include <cstring>
 11 #include <stdexcept>
 12 
 13 using std::istringstream;
 14 using std::set;
 15 using std::string;
 16 using std::getline;
 17 using std::map;
 18 using std::vector;
 19 using std::cerr;
 20 using std::cout;
 21 using std::cin;
 22 using std::ifstream;
 23 using std::endl;
 24 using std::ispunct;
 25 using std::tolower;
 26 using std::strlen;
 27 using std::out_of_range;
 28 
 29 string TextQuery::text_line(line_no line) const
 30 {
 31     if (line < lines_of_text.size())
 32         return lines_of_text[line];
 33     throw std::out_of_range("line number out of range");
 34 }
 35 
 36 // read input file: store each line as element in lines_of_text 
 37 void TextQuery::store_file(ifstream &is)
 38 {
 39     string textline;
 40     while (getline(is, textline))
 41        lines_of_text.push_back(textline);
 42 }
 43 
 44 // v: vertical tab; f: formfeed; 
: carriage return are
 45 // treated as whitespace characters along with space, tab and newline
 46 string TextQuery::whitespace_chars(" 	
v
f");
 47 
 48 // finds whitespace-separated words in the input vector
 49 // and puts the word in word_map along with the line number
 50 void TextQuery::build_map()
 51 {
 52     // process each line from the input vector
 53     for (line_no line_num = 0; 
 54                  line_num != lines_of_text.size();
 55                  ++line_num)
 56     {
 57         // we'll use line to read the text a word at a time
 58         istringstream line(lines_of_text[line_num]);
 59         string word;
 60         while (line >> word)
 61             // add this line number to the set;
 62             // subscript will add word to the map if it's not already there
 63             word_map[cleanup_str(word)].insert(line_num);
 64     }
 65 }
 66 
 67 set<TextQuery::line_no>
 68 TextQuery::run_query(const string &query_word) const
 69 {
 70     // Note: must use find and not subscript the map directly
 71     // to avoid adding words to word_map!
 72     map<string, set<line_no> >::const_iterator 
 73                           loc = word_map.find(cleanup_str(query_word));
 74     if (loc == word_map.end()) 
 75         return set<line_no>();  // not found, return empty set
 76     else
 77         // fetch and return set of line numbers for this word
 78         return loc->second;
 79 }
 80 
 81 void TextQuery::display_map()
 82 {
 83     map< string, set<line_no> >::iterator iter = word_map.begin(),
 84                                        iter_end = word_map.end();
 85 
 86     // for each word in the map
 87     for ( ; iter != iter_end; ++iter) {
 88         cout << "word: " << iter->first << " {";
 89 
 90         // fetch location vector as a const reference to avoid copying it
 91         const set<line_no> &text_locs = iter->second;
 92         set<line_no>::const_iterator loc_iter = text_locs.begin(),
 93                                      loc_iter_end = text_locs.end();
 94 
 95         // print all line numbers for this word
 96         while (loc_iter != loc_iter_end)
 97         {
 98             cout << *loc_iter;
 99 
100             if (++loc_iter != loc_iter_end)
101                  cout << ", ";
102 
103          }
104 
105          cout << "}
";  // end list of output this word
106     }
107     cout << endl;  // finished printing entire map
108 }
109 
110 string TextQuery::cleanup_str(const string &word)
111 {
112     string ret;
113     for (string::const_iterator it = word.begin(); it != word.end(); ++it) {
114         if (!ispunct(*it))
115             ret += tolower(*it);
116     }
117     return ret;
118 }
原文地址:https://www.cnblogs.com/burness/p/3235795.html