题目来源:C++ Primer 10.6题目。
文本查询程序,读取一段文本输入单词,获得该单词在文本中的行号,并输出该行的内容(若该单词在一行出现多次,只输出一次)
TextQuery.h
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include<string>
#include<vector>
#include<map>
#include<cctype>
#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;
class TextQuery{
public:
//类型别名
typedef vector<string>::size_type line_no;
typedef string::size_type str_size;
//接口
//read_file建立给定文件的内部数据结构
void
read_file(ifstream &is){
store_file(is);
build_map();
}
//查询给定单词并返回该单词所在行的行号集合
vector<line_no>
run_query(const string&)const;
//返回输入文件中指定行号对应的行
string
text_line(line_no) const;
private:
//保存输入文件
void store_file(ifstream&);
//将每个单词与一个行号集合想关联
void build_map();
//保存输入文件
vector<string> lines_of_text;
//将单词与出现该单词行的行号集合相关联
map<string,vector<line_no> > word_map;
//去掉标点并将字母变成小写
static std::string
cleanup_str(const std::string&);
};
#endif
TextQuery.cpp
#include "TextQuery.h"
#include <sstream>
#include<stdexcept>
string
TextQuery::text_line(line_no line)const{
if(line<lines_of_text.size())
return lines_of_text[line];
throw out_of_range("line number out of range");
}
//读入文件,将每行存储为lines_of_text的一个元素
void
TextQuery::store_file(ifstream& is){
string textline;
while(getline(is,textline))
lines_of_text.push_back(textline);
}
//在输入vector中找以空白为间隔的单词,将单词以及出现该单词的行的行号一起放入word_map
void
TextQuery::build_map(){
//处理输入vector中的每一行
for(line_no line_num=0;line_num!=lines_of_text.size();++line_num){
//一次读一个单词
istringstream line(lines_of_text[line_num]);
string word;
while(line>>word){
//去掉标点
word=cleanup_str(word);
//将行号加入到vector容器中
if(word_map.count(word)==0)
word_map[word].push_back(line_num);
else {
if(line_num!=word_map[word].back())
word_map[word].push_back(line_num);
}
}
}
}
vector<TextQuery::line_no>
TextQuery::run_query(const string &query_word)const{
map<string,vector<line_no> >::const_iterator
loc=word_map.find(query_word);
if(loc==word_map.end())
return vector<line_no>();//返回个空的vector对象
else
return loc->second;
}
//去掉标点并将字母变成小写
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;
}
主函数main.cpp
#include "TextQuery.h"
#include <stdlib.h>
string
make_plural(size_t ctr,const string &word,const string &ending){
return (ctr==1)?word:word+ending;
}
//打开输入文件流in并绑定到给定的文件
ifstream&
open_file(ifstream &in,const string &file){
in.close();
in.clear();
in.open(file.c_str());
return in;
}
void
print_results(const vector<TextQuery::line_no>& locs,
const string& sought,const TextQuery& file){
typedef vector<TextQuery::line_no> 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.text_line(*it)<<endl;
}
}
int main(int argc,char** argv){
//open the file from which user will query words
ifstream infile;
if(argc!=2||!open_file(infile,argv[1])){
cerr<<"No input file!"<<endl;
return EXIT_FAILURE;
}
TextQuery tq;
tq.read_file(infile);//建立map容器,逐行保存输入流中文件内容
//循环接受用户的查询要求并输出结果
while(true){
cout<<"enter word to look for, or q to quit: ";
string s ;
cin>>s;
string ret;
for(string::const_iterator it=s.begin();it!=s.end();++it){
ret+=tolower(*it);
}
s=ret;
//如果用户输入文件结束符或字符‘q’及'Q',则结束循环
if(!cin||s=="q"||s=="Q") break;
//获取吹安所查询单词所有行的行号
vector<TextQuery::line_no> locs=tq.run_query(s);
//输出出现次数及所有相关文本行
print_results(locs,s,tq);
}
return 0;
}
objects=main.o TextQuery.o
run:$(objects)
g++ -o run $(objects)
main.o:TextQuery.o
TextQuery.o:
.PHONY:clean
clean:
rm run $(objects)