12-30
要实现的功能:
- 从文件读入文本,并保存以用于查找
- 给读入的文本建立一个从单词到其出现的全部行号的映射
- 读取用户输入的单词,查找该单词是否出现过,如果出现过打印出所在行和行号
根据上面列出来的基本需求,决定应该使用哪些数据结构:
-
首先我们需要一个容器来保存从文件中读取的文本,考虑到查找是以行为单位进行的,因此文本逐行保存,
我们不妨用一个vector<string>来保存
-
因为要建立单词到行号的映射,所以肯定需要一个map,而行号又不能重复,所以宜采用set,最终推断其类型应该是
map<string,set<vector<string>::size_type>>
-
为了更加规范,我们把一些操作和数据封装到相应的类中,一个TextQuery类用来执行文件读取,查询操作,另一个QueryResult类用来保存查询结果。
-
查询结果包含行号,和行的内容,这样我们就需要在QueryResult类中访问TextQuery类的数据,考虑到在本章中前面小节的实践经验,我们不难想到此处要求两个类共同维护一些相同的数据对象,使用动态内存分配创建对象,再用智能指针shared_ptr来管理是一个很好地选择。
-
鉴于以上考虑我们要使用这样几个类型:
shared_ptr<vector<string>>
shared_ptr<set<vector<string>::size_type>>
map<string, shared_ptr<set<vector<string>::size_type>>>
基本的思路就是这样,下面就可以实现代码了:
#include<iostream>
#include<fstream>
#include<memory>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<string>
#include<sstream>
using namespace std;
class QueryResult;
class TextQuery
{
//内部数据成员
private:
//文件文本
shared_ptr <vector<string>> file;
//行号
shared_ptr<set<vector<string>::size_type>> lineNo;
//映射
map<string, shared_ptr<set<vector<string>::size_type>>> mapping;
//内部方法
private:
//对外提供接口
public:
//构造
TextQuery(ifstream &infile);
QueryResult query(string word);
};
class QueryResult
{
private:
//行号
shared_ptr<set<vector<string>::size_type>> lineNo;
//文件文本
shared_ptr <vector<string>> file;
string word;
public:
QueryResult() = default;
ostream& print();//打印查找结果
QueryResult(shared_ptr<set<vector<string>::size_type>> s,
shared_ptr < vector<string>> v, string word);
};
ostream& QueryResult::print()
{
if (!lineNo) {
cout << "nothing found!" << endl;
return cout;
}
cout << word << " is found " << lineNo->size() << " times:" << endl;
for (auto num : *lineNo) {
cout << "(at line " << num << " )\t" << (*file)[num - 1] << endl;
}
return cout;
}
QueryResult::QueryResult(shared_ptr<set<vector<string>::size_type>> s,
shared_ptr < vector<string>> v, string word) :file(v), lineNo(s),word(word) {}
//构造函数
TextQuery::TextQuery(ifstream &infile):file(new vector<string>)
{
vector<string>::size_type number = 1;
for (string line; getline(infile, line);++number) {
file->push_back(line);//把当前行放入vector
istringstream instring(line);
for (string word; instring >> word;) {
//下标访问map,如果键不存在会新建,值进行默认初始化(内置类型值初始化,类类型调用默认构造函数)
auto &a = mapping[word]; //这里要用引用,否则赋值会引起引用计数增加
if (!a) {
a.reset(new set<vector<string>::size_type>({number}));
}
a->insert(number);
}
}
}
QueryResult TextQuery::query(string word)
{
shared_ptr<set<vector<string>::size_type>> none;
auto it = mapping.find(word);
//如果找到了这个单词
if (it != mapping.end()) {
return(QueryResult(it->second, file, word));
}
else {
return(QueryResult(none, file, word));
}
}
//查找单词
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
while (true) {
std::cout << "enter word to look for, or q to quit: ";
string word;
if (cin>>word and word!="q") {
//开始查询
tq.query(word).print()<<endl;
}
}
}
int main(int argc, char*argv[])
{
ifstream infile("../12-30/main.cpp");
runQueries(infile);
system("pause");
return 0;
}
喔,浪费了好长时间