C++Primer中一个简单的文本单词查询小程序
TextQuery头文件:
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <cstring>
#include <cctype>
using namespace std;
class TextQuery
{
public:
// typedef to make declarations easier
TextQuery(void);
virtual ~TextQuery(void);
typedef string::size_type str_size;
typedef vector<string>::size_type LineNo;
/* 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 ReadFile(ifstream &is)
{ StoreFile(is);BuildMap();}
set<LineNo> RunQuery(const string&) const;
string TextLine(LineNo) const;
str_size size() const
{ return LinesText.size(); }
void DisplayMap(); // debugging aid: print the map
private:
// utility functions used by read_file
void StoreFile(ifstream&); //store input file
void BuildMap(); //associated each word with a set of line numbers
vector<string> LinesText; // remember the whole input file
map<string, set<LineNo> > word_map; // map word to set of the lines on which it occurs
static string whitespace_chars;
// canonicalizes text: removes punctuation and makes everything lower case
static string cleanup_str(const string&);
};
#endif
TextQuery源文件:
#include "TextQuery.h"
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
#include <stdexcept>
using namespace std;
TextQuery::TextQuery(void)
{
}
TextQuery::~TextQuery(void)
{
}
set<TextQuery::LineNo> TextQuery::RunQuery(const string &QueryWord) const
{
// Note: must use find and not subscript the map directly
// to avoid adding words to word_map!
map<string, set<LineNo> >::const_iterator loc = word_map.find(QueryWord);
if (loc == word_map.end())
{
return set<LineNo>(); // not found,return empty set
}
else
{
return loc->second; //fetch and return set of line numbers for this word
}
}
// read input file: store each line as element in LinesText
void TextQuery::StoreFile(ifstream &is)
{
string textline;
while (getline(is, textline))
{
LinesText.push_back(textline);
}
}
// \v: vertical tab; \f: formfeed; \r: carriage return are
// treated as whitespace characters along with space, tab and newline
string TextQuery::whitespace_chars(" \t\n\v\r\f");
string TextQuery::TextLine(LineNo line) const
{
if (line < LinesText.size())
{
return LinesText[line];
}
throw out_of_range("line number out of range");
}
// finds whitespace-separated words in the input vector
// and puts the word in word_map along with the line number
void TextQuery::BuildMap()
{
// process each line from the input vector
for (LineNo line_num = 0; line_num != LinesText.size(); ++line_num)
{
// we'll use line to read the text a word at a time
istringstream line(LinesText[line_num]);
string word;
while (line>>word)
{
word_map[cleanup_str(word)].insert(line_num); // add this line number to the set;
}
}
}
void TextQuery::DisplayMap()
{
map< string, set<LineNo> >::iterator iter = word_map.begin(),
iter_end = word_map.end();
// for each word in the map
for ( ; iter != iter_end; ++iter) {
cout << "word: " << iter->first << " {";
// fetch location vector as a const reference to avoid copying it
const set<LineNo> &text_locs = iter->second;
set<LineNo>::const_iterator loc_iter = text_locs.begin(),
loc_iter_end = text_locs.end();
// print all line numbers for this word
while (loc_iter != loc_iter_end)
{
cout << *loc_iter;
if (++loc_iter != loc_iter_end)
cout << ", ";
}
cout << "}\n"; // end list of output this word
}
cout << endl; // finished printing entire map
}
// lower-case to upper-case
string TextQuery::cleanup_str(const string &word)
{
string ret;
for (string::const_iterator it = word.begin(); it != word.end(); ++it) {
if (!ispunct(*it))
ret += tolower(*it);
}
return ret;
}
主程序:
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include "TextQuery.h"
using namespace std;
//open file
ifstream& OpenFile(ifstream &in, const string &file)
{
in.close(); //close in case it was already open
in.clear(); //clear any existing errors
in.open(file.c_str()); //open the file we were given
return in;
}
//put words' odd or plural
string make_plural(size_t ctr, const string &word, const string &ending)
{
return ( ctr == 1) ? word : (word+ending);
}
//print the Results
void PrintResults(const set<TextQuery::LineNo>& locs,
const string &sought, const TextQuery &file)
{
typedef set<TextQuery::LineNo> line_nums;
line_nums::size_type size = locs.size();
cout<<"\n"<<sought<<" occurs "<<size<<" "<<make_plural(size, "time", "s")<<endl;
line_nums::const_iterator it = locs.begin();
for (; it != locs.end(); ++it)
{
cout<<"'\t(line "<<(*it)+1<<")"<<file.TextLine(*it)<<endl;
}
}
int main(int argc, char *argv[])
{
//open the from which user will query words
ifstream infile;
if (!OpenFile(infile, "tianqi.txt"))
{
cerr << "No input file!" << endl;
return EXIT_FAILURE;
}
TextQuery tq;
tq.ReadFile(infile); //builds query map
while (true)
{
cout<<"enter word to look for, or q to quit";
string s;
cin>>s; //error:debbug here will be stoped why????
//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::LineNo>locs = tq.RunQuery(s);
//print count and all occurences, if any
PrintResults(locs, s, tq);
}
return 0;
}
这个程序还有一个调试错误!