C++ Primer 5th 第12章动态内存
使用标准库:文本查询程序
程序名称:文本查询程序
程序功能:允许用户在一个给定文件中查询单词,查询结果是单词在文件
中出现的次数及其所在行的列表.如果一个单词在一行中出现多次,此
行只列出一次。行会按照升序排列输出
输出格式:
element occurs 112 times
(line 36) Aset element contains only a key;
程序需完成的操作:
- 当程序读入一个输入文件时,必须记住单词出现的每一行。因此,
程序需要逐行读取输入文件,并将每一行分解为独立的单词 - 当程序生成输出时,
1)必须能够提取每个单词所关联的行号
2)行号必须按升序出现且无重复
3)必须能打印给定行号中的文本
程序实现:
1.使用vector<string>来保存整个输入文件的一份拷贝。输入文件中
的每一行保存为vector<string>中的一个元素。当需要打印一行时,可
用行号作为下标提取行文本
2.使用isstringstream来将每行分解为单词。
3.使用一个set来保存每个单词在输入文件中出现的行号。这保证了每行只
出现一次且行号按升序保存
4.使用一个map来将每个单词与它出现的行号set关联起来。
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <memory>
#include <sstream>
#include <set>
#include <map>
#include <stdexcept>
using namespace std;
/*
QueryResult类:
保存查询结果:包括查询的单词
行号集合 和 文本拷贝 指针
操作:
定义print为类的友元函数
构造函数接受一个
*/
/*
TextQuery类:
数据成员:2个成员vector和map分别用来保存输入文件的文本,map用来
关联每个单词和它出现的行号的set
查询操作:返回给定单词的查询结果
*/
using line_no = vector<string>::size_type;
string make_plural(size_t ctr, const string& word,
const string& ending);
class QueryResult; //为了定义query的返回类型,提要提前声明,告知编译器
class TextQuery {
public:
TextQuery(ifstream &);
QueryResult query(const string& word);
// ~TextQuery();
private:
shared_ptr<vector<string>> file;
map<string, shared_ptr<set<line_no>>> wm;//给定单词到指向行集合智能指针的映射
};
class QueryResult {
friend ostream& print(ostream& os, const QueryResult& result);
public:
QueryResult(string, shared_ptr<vector<string>>,
shared_ptr<set<line_no>>);
// ~QueryResult();
private:
string sought;//保存要查询的单词
shared_ptr<vector<string>> file;//指向保存输入文件的指针
shared_ptr<set<line_no>> lines;//指向保存行号的set
};
void runQueries(ifstream& infile) {
//infile是一个ifsteram
TextQuery tq(infile); //保存文件并建立查询map
//与用户交互:提示用户输入要查询的单词,完成查询并打印结果
while (true) {
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s ) || s == "q") {
break;
}
//指向查询并打印结果
print(cout, tq.query(s)) << endl;
}
}
//TextQuery 构造函数
//读取输入文件并建立单词到行号的映射
TextQuery::TextQuery(ifstream& is): file(new vector<string>) {
string Text;
while (getline(is, Text)) { //对每行文本进行操作
file->push_back(Text); //保存此行文本
line_no n = file->size()-1; //当前行号 = 保存每行文本的vector的大小-1
istringstream line(Text); //string输入流
string word;
while (line >> word) {
//map<string, shared_ptr<set<line_no>>> wm;
auto &lines = wm[word]; // line是一个shared_ptr;
if (!lines) { //第一次遇见该单词,指针为空
/*reset是智能指针的成员函数,
shared_ptr<T> p.reset(q) 另p指向q
*/
lines.reset(new set<line_no>); //分配一个新的set
}
lines->insert(n);//将本单词出现的行号插入到行号集合中
}
}
}
//TextQuery 查询操作query,返回指向给定单词所在行号的集合 和 输入文件的智能指针
QueryResult TextQuery::query(const string &sought) {
//如果没有找到sought,返回一个指向此set的指针(空)
static shared_ptr<set<line_no>> nodata(new set<line_no>);
//使用下标运算符来查找单词,可能会将不存在映射的关系的单词强行加入
//使用find而不是下标运算符来查找单词,避免将单词添加到wm中
auto loc = wm.find(sought);
if (loc == wm.end()) {
//Effective C++ 必须返回对象时,别妄想返回其reference
return QueryResult(sought, file, nodata);
} else {
return QueryResult(sought, file, loc->second);
}
}
//QueryResult的构造函数唯一任务就是将参数保存到对应的数据成员中
QueryResult::QueryResult(string word, shared_ptr<vector<string>> file,
shared_ptr<set<line_no>> lines): sought(word),
file(file), lines(lines) {
}
//QueryResult 的友元函数, 赋予其访问参数QueryResule的私有数据成员
//打印result
ostream& print(ostream& os, const QueryResult& result) {
//string sought;//保存要查询的单词
//shared_ptr<vector<string>> file;//指向保存输入文件的指针
//shared_ptr<set<line_no>> lines;//指向保存行号的set
//make_plural函数是根据第一个参数的大小,确定是输出time 还是 times
os << result.sought << " occurs " << result.lines->size() << " "
<< make_plural(result.lines->size(), "time", "s") << endl;
for (auto num : *result.lines) {
os << "\t(line " << num + 1 << ")"
//file->begin()+num 即为file指向的vector
//中第num个元素的位置,解引用是所在行号对应的输入文本
<< *(result.file->begin() + num) << endl;
}
}
string make_plural(size_t ctr, const string& word,
const string& ending) {
return (ctr > 1 ) ? word + ending : word;
}
//TextQuery::~TextQuery() {
//}
//QueryResult::~QueryResult() {
//}
int main(int argc, char *argv[]) {
if (argc != 2) {
throw runtime_error("参数错误!");
}
ifstream in(argv[1]);
if (!in) {
throw runtime_error("input file is no open!");
}
runQueries(in);
return 0;
}