书上的一个比较简单的例题,可以输出文本中单词总共出现的次数,所在的行数及所在行的内容。
主要的思路是创建一个vector来分行存储文本,然后将单词及单词出现行数(set)创建一个map。通过查询单词获得其映射的set。set的size为出现的次数,set的元素为出现的行数,通过set元素作为vector下标获得所在行内容。
1、TextQuery.h
#pragma once
#include <fstream>
#include <iostream>
#include <vector>
#include <memory>
#include <map>
#include <set>
#include <string>
#include <sstream>
//不要在头文件里包含太多头文件,一般包含在cpp中
using namespace std;
class QueryResult; //先声明后使用。要返回内容多,最简单的方法是定义一个类
class TextQuery
{
public:
using line_no = vector<string>::size_type; //类型成员,必须先定义后使用
TextQuery(ifstream &); //构造函数,传入要查询的文本
QueryResult query(const string&)const; //查询函数,传入要查询的单词,返回查询结果
~TextQuery();
private:
shared_ptr<vector<string>> file; //输入文件
map<string, shared_ptr<set<line_no>>> wm; //单词到行号集合的映射
};
2、TextQuery.cpp
#include "TextQuery.h"
#include "QueryResult.h"
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) //从每行中读取单个单词
{
auto &lines = wm[word]; //map的下标运算符,如果关键字不存在,则创建一个新元素。
//其value为空指针
//lines是引用,改变它也会改变智能指针
if (!lines)
{
lines.reset(new set<line_no>); //第一次遇到word为空指针,因此让他指向新的set
}
lines->insert(n); //将行号插入set,如果重复则不执行任何操作
}
}
}
TextQuery::~TextQuery()
{
}
QueryResult TextQuery::query(const string &sought)const
{
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc==wm.end())
{
return QueryResult(sought, nodata, file); //如果没找到,返回空set
}
else
{
return QueryResult(sought, loc->second, file); //返回指向set的指针
}
}
3、QueryResult.h
#pragma once
#include "TextQuery.h"
class QueryResult
{
friend ostream &print(ostream &, const QueryResult &);
public:
QueryResult(string s,shared_ptr<set<TextQuery::line_no>> p,shared_ptr<vector<string>> f):
sought(s),lines(p),file(f){}
private:
string sought; //查询的单词
shared_ptr<set<TextQuery::line_no>> lines; //包含的行数
shared_ptr<vector<string>> file; //输入文件
};
4、main.cpp
#include "QueryResult.h"
#include "TextQuery.h"
int main()
{
void runQuery(ifstream &file);
ifstream file("file.txt");
runQuery(file);
return 0;
}
ostream &print(ostream &os, const QueryResult &qr)
{
os << qr.sought << " occurs " << qr.lines->size() <<" times"<< endl;
for (auto num:*qr.lines)
{
os << "\t(line" << num + 1 << ") " << *(qr.file->begin() + num) << endl;
}
return os;
}
void runQuery(ifstream &file)
{
TextQuery tq(file);
while (true)
{
cout << "enter word to look for,or q to quit"<<endl;
string s;
if (!(cin>>s)||s=="q")
{
break;
}
print(cout, tq.query(s)) << endl;
}
}