3ed_query.cpp
上传用户:qdkongtiao
上传日期:2022-06-29
资源大小:356k
文件大小:10k
源码类别:

书籍源码

开发平台:

Visual C++

  1. /*
  2.  * This file contains code from "C++ Primer, Fourth Edition", by Stanley B.
  3.  * Lippman, Jose Lajoie, and Barbara E. Moo, and is covered under the
  4.  * copyright and warranty notices given in that book:
  5.  * 
  6.  * "Copyright (c) 2005 by Objectwrite, Inc., Jose Lajoie, and Barbara E. Moo."
  7.  * 
  8.  * 
  9.  * "The authors and publisher have taken care in the preparation of this book,
  10.  * but make no expressed or implied warranty of any kind and assume no
  11.  * responsibility for errors or omissions. No liability is assumed for
  12.  * incidental or consequential damages in connection with or arising out of the
  13.  * use of the information or programs contained herein."
  14.  * 
  15.  * Permission is granted for this code to be used for educational purposes in
  16.  * association with the book, given proper citation if and when posted or
  17.  * reproduced.Any commercial use of this code requires the explicit written
  18.  * permission of the publisher, Addison-Wesley Professional, a division of
  19.  * Pearson Education, Inc. Send your request for permission, stating clearly
  20.  * what code you would like to use, and in what specific way, to the following
  21.  * address: 
  22.  * 
  23.  *  Pearson Education, Inc.
  24.  *  Rights and Contracts Department
  25.  *  75 Arlington Street, Suite 300
  26.  *  Boston, MA 02216
  27.  *  Fax: (617) 848-7047
  28. */ 
  29. #include "3ed_query.h"
  30. #include <string>
  31. #include <vector>
  32. #include <map>
  33. #include <set>
  34. #include <iostream>
  35. #include <fstream>
  36. #include <cctype>
  37. #include <cstring>
  38. using std::set;
  39. using std::string;
  40. using std::getline;
  41. using std::map;
  42. using std::vector;
  43. using std::cerr;
  44. using std::cout;
  45. using std::cin;
  46. using std::ifstream;
  47. using std::endl;
  48. using std::pair;
  49. using std::make_pair;
  50. using std::ispunct;
  51. using std::tolower;
  52. using std::strlen;
  53. // read input file: store each line as element in lines_of_text 
  54. void TextQuery::store_file(ifstream &is)
  55. {
  56.     string textline;
  57.     while (getline(is, textline))
  58.        lines_of_text.push_back(textline);
  59. }
  60. set<string> TextQuery::exclusion_set = build_exclusion_set();
  61. // v: vertical tab; f: formfeed; r: carriage return are
  62. // treated as whitespace characters along with space, tab and newline
  63. string TextQuery::whitespace_chars(" tnvrf");
  64. // finds the whitespace separated words in the input vector
  65. // then puts them in a cannonical form: eliminate suffixes,
  66. // make lowercase etc. Finally, test if its an excluded word or not.
  67. // If not excluded store the word in word_map along with
  68. // its location (line # and character position) info
  69. void TextQuery::build_map()
  70. {
  71.     // process each line from the input vector
  72.     for (line_no line_num = 0; 
  73.                  line_num != lines_of_text.size();
  74.                  ++line_num)
  75.     {
  76.         string textline = lines_of_text[line_num];
  77.         if (textline.empty()) continue; //ignore blank lines
  78.         // make line lower case and remove extraneous punctuation
  79.         strip_caps(textline);
  80.         strip_punct(textline);
  81.         /* 
  82.          * Because we want to keep track of position as well as
  83.          * line number, we have to process the line a character at a time.
  84.          * We can't use an istringstream to read the words.
  85.          * prev_pos will denote first character in each word,
  86.          * pos will denote whitespace that separates the word from the next
  87.          * initially pos denotes first non-whitespace at beginning of line
  88.         */
  89.         str_size pos = skip_whitespace(textline, 0), prev_pos = pos;
  90.         // find each whitespace separated word
  91.         while ((pos = textline.find_first_of(whitespace_chars, pos)) 
  92.                    != string::npos)
  93.         {
  94.             // remove suffixes and put the word into the map if apporpriate
  95.             test_insert(textline, prev_pos, pos, line_num);
  96.             // if there's more text to process, increment pos to get next char
  97.             if (pos != textline.size())
  98.                 ++pos;  
  99.             // read and discard adjacent spaces, if any, updating prev_pos too
  100.             pos = prev_pos = skip_whitespace(textline, pos);
  101.         }
  102.         // don't forget last word in the line
  103.         if (pos != prev_pos)  // false if line ends in whitespace
  104.             test_insert(textline, prev_pos, pos, line_num);
  105.     }
  106. }
  107. TextQuery::str_size 
  108. TextQuery::skip_whitespace(const string &line, str_size pos)
  109. {
  110.     // ignore adjacent whitespace
  111.     str_size next = line.find_first_not_of(whitespace_chars, pos);
  112.     if (next != string::npos)
  113.         return next;
  114.     else
  115.         return line.size();
  116. }
  117. void TextQuery::test_insert(const string &line, str_size prev_pos, 
  118.                   str_size pos, line_no line_num)
  119. {
  120.     // make copy of the whitespace delimited word
  121.     string word(line.substr(prev_pos, pos - prev_pos));
  122.     strip_suffixes(word); // last of the cleanup operations
  123.     // if there's anything left after stripping punctuation 
  124.     // and it's not an excluded word, add to the map
  125.     // appending line num & char pos to vector for this word
  126.     if (!word.empty() && !exclude_word(word))
  127.          word_map[word].push_back(make_pair(line_num,prev_pos));
  128.     return;
  129. }
  130. void TextQuery::strip_suffixes(string &word)
  131. {
  132.      if (word.size() <= 3)    // too short to have any suffixes
  133.             return;
  134.      if (word[word.size() - 1] == 's')  // only handle plurals so far
  135.              suffix_s(word);
  136.      // additional suffix handling goes here
  137. }
  138. void TextQuery::suffix_s(string &word)
  139. {
  140.     // some words ending in s aren't suffixes, they're part of the word
  141.     static char* ok_endings[] = {"ous", "ius", "ss", "is"};
  142.     size_t sz = sizeof(ok_endings)/sizeof(char*);  // how many elements?
  143.     for (size_t i = 0; i != sz; ++i)
  144.         if (chk_ending(word, ok_endings[i]) == 0)
  145.               return;
  146.     // replace common suffixes by their base word ending
  147.     // repl_endings first dimension is the ending we'll remove
  148.     //              second dimension is the new ending we'll insert
  149.     static char* repl_endings[][2] = 
  150.           { {"ies", "y"}, {"ses", "s"}, {"'s", ""}, {"s", ""} };
  151.     sz = sizeof(repl_endings)/(sizeof(char*) * 2);  // two-dimensions
  152.     for (size_t i = 0; i != sz; ++i) 
  153.         if (chk_ending(word, repl_endings[i][0]) == 0) {
  154.            size_t sz = strlen(repl_endings[i][0]);
  155.            word.replace(word.size() - sz, sz, repl_endings[i][1]);
  156.            return;
  157.         }
  158. }
  159. // compare end of the word with the ending we're given
  160. int TextQuery::chk_ending(const string &word, const char *ending)
  161. {
  162.     size_t sz = strlen(ending);
  163.     return word.compare(word.size() - sz, sz, ending);
  164. }
  165. void TextQuery::strip_caps(string &line)
  166. {
  167.     // not changing the size of line, so safe to cache the size
  168.     str_size sz = line.size();
  169.     for (str_size pos = 0; pos != sz; ++pos)
  170.         line[pos] = tolower(line[pos]);
  171. }
  172. // except for apostrophe, replace punctuation by a space
  173. // apostrophe is special: it might precede by 's, which is a suffix
  174. void TextQuery::strip_punct(string &line)
  175. {
  176.     for (str_size pos = 0; pos != line.size(); ++pos)
  177.         if (ispunct(line[pos])) {
  178.              if (line[pos] != ''') line[pos] = ' ';
  179.         }
  180. }
  181. // this function should be changed to let the user specify a
  182. // file of words to ignore, but for now, we'll keep it simple
  183. // and assume a specific file
  184. set<string> TextQuery::build_exclusion_set()
  185. {
  186.     set<string> ret;
  187.     ifstream infile("exclusion_set");
  188.     if (!infile)
  189.     {
  190.         static string default_excluded_words[] = {
  191.           "the","and","but","that","then","are","been",
  192.           "can","can't","cannot","could","did","for",
  193.           "had","have","him","his","her","its","into",
  194.           "were","which","when","with","would"
  195.          };
  196.         cerr << "warning! unable to open word exclusion file! -- "
  197.             << "using default set" << endl;
  198.         ret = set<string>(default_excluded_words, 
  199.                           default_excluded_words + 
  200.                           sizeof(default_excluded_words)/sizeof(string));
  201.     } else {
  202.         string word;
  203.         while (infile >> word) ret.insert(word);
  204.     }
  205.     return ret;
  206. }
  207. bool TextQuery::exclude_word(const string &word)
  208. {
  209.     return (exclusion_set.find(word) != exclusion_set.end());
  210. }
  211. vector<TextQuery::location> TextQuery::run_query(const string &s)
  212. {
  213.     // make local copy so we can clean it up to match words
  214.     // entered in the map; but when communicate back to the
  215.     // user always use their original version
  216.     string sought = s;  
  217.     strip_caps(sought);
  218.     strip_punct(sought);
  219.     strip_suffixes(sought);
  220.     // Note: must use find and not subscript the map directly
  221.     // Subscripting a map adds the element if it's not already there
  222.     // We want to know whether the element was there to begin with
  223.     if (word_map.find(sought) == word_map.end())
  224.          return vector<location>();  // not found, return empty location vector
  225.     else
  226.         // fetch list of locations for this word
  227.         return word_map[sought];
  228. }
  229. void TextQuery::display_map()
  230. {
  231.     typedef map< string,vector<location> > map_text;
  232.     map_text::iterator iter = word_map.begin(),
  233.                    iter_end = word_map.end();
  234.     // Note: map iter returns index, value pair
  235.     // so iter->first is the index word, 
  236.     // iter->second the vector of its locations
  237.     // for each word in the map
  238.     while (iter != iter_end) {
  239.         cout << "word: " << iter->first << " {";
  240.         vector<location> text_locs = iter->second;
  241.         vector<location>::iterator liter = text_locs.begin(),
  242.                                liter_end = text_locs.end();
  243.         // print all (line,char) locations for this word
  244.         // because this is a debugging routine, don't adjust
  245.         // line/pos numbers.  As normal for programmers, start from 0
  246.         while (liter != liter_end)
  247.         {
  248.             cout << "(" << liter->first
  249.                  << "," << liter->second << ")";
  250.             if (++liter != liter_end)
  251.                  cout << ", ";
  252.          }
  253.          cout << "}n";  // end list of output this word
  254.          ++iter;         // get next word in the map
  255.     }
  256.     cout << endl;  // finished printing entire map
  257. }