概述
我们将实现一个简单的文本查询程序,我们的程序允许用户在一个给定文件中查询单词。查询结果是单词在文件中出现的次数及其所在行的列表。如果一个单词在一行中出现多次,此行只列出一次。行会按照升序输出。
我们可以直接使用vector 、set和map来直接编写文本查询程序,我们在这里经定义一个抽象的解决方案。首先我们将会定义一个保存输入文件的类,它包含一个vector和map。vector用来保存输入文件的文本,map用来关联每个单词和他出现的行号的set。
1、直接使用vector、set 和map来编写文本查询程序
#include<iostream>
#include<fstream>
#include<sstream>
#include<vector>
#include<map>
#include<set>
using namespace std;
using line_no = vector<string>::size_type;
/*file和wm都定义成全局变量,便于在函数间共享*/
vector<string> file; //文件每行的内容
map<string, set<line_no>>wm; //单词到行号set的映射
string cleanup_str(const string &word)
{
string ret;
for (auto it = word.begin(); it != word.end(); ++it)
{
if (!ispunct(*it))
ret += tolower(*it);
}
return ret;
}
void input_text(ifstream &is)
{
string text;
while (getline(is, text))
{
file.push_back(text);
int n = file.size() - 1;
istringstream line(text);
string word;
while (line >> word)
{
wm[cleanup_str(word)].insert(n);
}
}
}
ostream &query_and_print(const string &sought, ostream&os)
{
auto loc = wm.find(sought);
if (loc == wm.end())
{
os << sought << "出现了0次" << endl;
}
else
{
auto lines = loc->second;
os << sought << " 出现了 " << lines.size() << "次" << endl;
for (auto num : lines)
{
os << "t(第" << num + 1 << "行)" << *(file.begin() + num) << endl;
}
}
return os;
}
void runqueries(ifstream& infile)
{
input_text(infile);
while (1)
{
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
query_and_print(s, cout) << endl;
}
}
int main()
{
ifstream in("file.txt");
if (in) {
runqueries(in);
}
else
cout << "couldn't open: " << endl;
return 0;
}
1、我们使用一个vector来保存整个文件的一份拷贝。
2、使用一个istringstream来将每行分解为单词。
3、使用一个set来保存每个单词在输入文本中出现的行号,保证了每行只出现一次,且按升序保存。
4、最后使用一个map就将单词与其对应的行号关联起来。
结果如下:
2、使用面向对象的思维来实现
首先我们从定义一个保存输入文件的类开始:
#pragma once
#include<map>
#include<string>
#include<memory>
#include<vector>
#include<fstream>
#include<set>
#include<sstream>
#include<iostream>
using namespace std;
class QueryResult; //这句声明必须要
class Textquery {
public:
using line_no = vector<string>::size_type;
Textquery(ifstream&);
QueryResult query(const string&) const;
private:
shared_ptr<vector<string>> file;//输入文件
map<string, shared_ptr<set<line_no>>> wm;//每个单词到他所在行号的映射
};
我们定义的保存文件的类中包含两个私有成员一个vector和一个map,与上述方法一类似我们使用以个vector来保存输入的文本文件,map用来关联每个单词和他出现的行号set。
并且将定义一个构造函数,从给定的输入文件来构造此对象如下:
#include "Textquery.h"
Textquery::Textquery(ifstream&is) :file(new vector<string>)
{
string text;
while (getline(is, text))
{
//cout << text << endl;
file->push_back(text); //保存文本中的每一行
int n = file->size() - 1; //当前行号
istringstream line(text);
string word;
while (line >> word) //处理行中的每一个单词
{
shared_ptr<set<line_no>> &lines = wm[word];
if (!lines) //当我们第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>); //分配一个新的set
lines->insert(n); //将此行号插入set中。
}
}
}
除此之外,我们在此类中定义了一个成员函数来执行查询的操作。查询操作要完成的任务十分简单:查找map成员,检查给定单词是否出现。这个函数将要返回我们所查询的结果(及出现的次数,行号,以及每行的文本),为此我们定义另外一个类来保存我们的查询结果,并且这个类可以打印对应查询的内容。
#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; //输入文件
};
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;
}
QueryResult Textquery::query(const string &sought) const
{
//如果没有找到sought,返回一个指向此set的指针
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);
else
return QueryResult(sought, loc->second, file);
}
最后测试一下:
#include "Textquery.h"
#include"QueryResult.h"
void runqueries(ifstream& infile)
{
Textquery tq(infile);
while (1)
{
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
print(cout, tq.query(s)) << endl;
}
}
int main()
{
ifstream in("file.txt");
if (in) {
runqueries(in);
}
else
cout << "couldn't open: " <<endl;
return 0;
}
与方法一不同的是,我们要在不同的类对象间共享数据,我们使用了shared_ptr来管理。
说明:我们的QueryResult类要表达的查询结果。这些数据都保存在Textquery类型的对象中。因此我们必须确定如何访问他们。我们可以拷贝行号set,但这样可能会很耗时。此外,我们不希望拷贝vector,因为这可能会影响整个文件的拷贝。通常返回指向Textquery对象内部的指针,我们可以避免拷贝操作,但是如果Textquery对象在QueryResult对象之前被销毁,我们将使用非法的内存,因此我们可以是用shared_ptr来处理这种共享问题。
最后
以上就是阔达时光为你收集整理的使用标准库:文本查询程序的全部内容,希望文章能够帮你解决使用标准库:文本查询程序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复