• 一个简单的文本查询程序


    需求:

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

    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 }
  • 相关阅读:
    关于在ubuntu12.04图形界面下不能从root用户直接登录的问题
    error: stray '357' in program
    关于gcc -o 的使用问题
    如何解决程序退出重启后不能绑定端口的问题?
    使用Ubuntu12.04登陆账户时,输入密码是正确的,但是图形界面闪一下后就又回到登陆页面了
    如何在linux系统中设置严密的密码策略(译文)
    sqlite3数据库归纳
    Bing地图切片原理
    CSS技巧
    jQuery.extend方法和开发中变量的复用
  • 原文地址:https://www.cnblogs.com/burness/p/3235795.html
Copyright © 2020-2023  润新知