#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
// 因为一个查询类TextQuery需要一个接受一个ifstream
// 以及返回一个QueryResult表示string出现的那些行 表示行号和查询的文本
// 考虑使用vector<string>保存每行文件和一个保存单词行号的set
// 并且set行号与vector中的元素要一一对应。所以需要一个map来映射set和string
// 其中对于set是使用一个shared_ptr来管理
// 对于上面的两种数据类型我们使用 shared_ptr来进行管理
//为了定义函数query返回的类型这里的声明是十分必要的
class QueryResult;
class TextQuery {
public:
using line_no = std::vector<std::string>::size_type;
//构造函数从文件流中读取并且建立行号和文本对应行的set
TextQuery(std::ifstream &);
// const成员函数用于返回一个带有行号和strig内容的QueryResult类型
QueryResult query(const std::string &) const;
private:
std::shared_ptr<std::vector<std::string>> file;
//每个单词到他的行号的映射
std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
};
//读取文件并且建立单词到行号之间的映射
TextQuery::TextQuery(ifstream &is) : file(new vector<string>) {
string text;
while (getline(is, text)) { //文件中的每一行
file->push_back(text); //加入到vector中保存当前行
int n = file->size() - 1; //当前的行号
istringstream line(text); //将当前行分解为单词
string word;
while (line >> word) { //对于每个单词
//如果单词不再wm中,以之为下标在wm中添加一项
auto &lines = wm[word]; //这里的line是一个shared_ptr
if (!lines) { //第一次遇到这个单词 此指针为空
lines.reset(new set<line_no>); //分配一个新的set
}
//将这个行插入到set中 如果是重复的单词是无法插入到set里面的
lines->insert(n);
}
}
}
// QueryResult类有三个数据成员
// 1. string用于保存查询的单词
// 2. 一个shared_ptr指向输入文件的vector(这样可以通过下标进行访问)
// 3. 一个shared_ptr指向保存单词出现的行号的set
// 唯一的成员函数是一个构造函数 初始化着三个数据成员
class QueryResult {
//返回一个输出流
friend std::ostream &print(std::ostream &, const QueryResult &);
using line_no = std::vector<std::string>::size_type;
public:
QueryResult(std::string s, std::shared_ptr<std::set<line_no>> p,
std::shared_ptr<std::vector<std::string>> f)
: sought(s), lines(p), file(f) {}
private:
std::string sought; //查询单词
std::shared_ptr<std::set<line_no>> lines; //出现的行号
std::shared_ptr<std::vector<std::string>> file; //输入文件
};
// query 函数
//接受一个string参数 即为查询单词
// const成员函数用于返回一个带有行号和strig内容的QueryResult类型
// QueryResult query(const std::string &) const;
QueryResult TextQuery::query(const string &sought) const {
//如果没有找到sought 我们将返回一个指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
//使用find而不是下标运算符来查找单词 避免将单词添加到wm中
auto loc = wm.find(sought);
if (loc == wm.end())
return QueryResult(sought, nodata, file); //没有找到
else
return QueryResult(sought, loc->second, file); //返回一个临时拷贝
}
string make_plural(size_t ctr, const string &word, const string &ending) {
return (ctr > 1) ? word + ending : word;
}
ostream &print(ostream &os, const QueryResult &qr) {
//如果找到了单词 打印出现的次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << " "
<< make_plural(qr.lines->size(), "time", "s") << endl;
//打印单词出现的每一行 遍历一个set
for (auto num : *qr.lines) { //对set中的每个单词
//避免行号从0开始
os << "\t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
}
return os;
}
void runQueries(ifstream &infile) {
// infile 是一个ifstream 指向我们要处理的文件
TextQuery tq(infile); //保存文件建立Map查询
//与用户交互
while (true) {
cout << "enter word to look for, or q to quit: ";
string s;
//遇到文件尾或者用户输入'q'时循环终止
if (!(cin >> s) || s == "q") {
break;
}
//指向查询并且打印结果
print(cout, tq.query(s)) << endl;
}
}
int main(int argc, char const *argv[]) {
ifstream fs("C:\\Users\\oyx\\Desktop\\algorithm\\test.txt",std::ios::in);
runQueries(fs);
//system("pause");
return 0;
}
cpp primer TextQuery练习
最新推荐文章于 2024-10-05 09:16:02 发布