题目:在给定文件中查询单词。查询结果是单词在文件中出现的次数及所在行的列表。如果一个单词在一行中多次出现,此行只显示一次,行会按照升序输出。
效果如下:
在此程序的实现中使用到的标准库内容包括:
(1)ifstream:用来绑定并打开指定的文本。
(2)vector<string>:用来保存整个文本,每行保存为vector中的元素。
(3)istringstream:用来将每行分解为单词。
(4)multiset:用来保存每个单词出现的行号,保证了行号按升序排列;一个单词在某行中出现多次,行号会重复保存;
(5)set:用来将multiset中的重复去除,用于一个单词在某行中出现多次,只显示一次;
(6)map:将单词和它出现的行号的multiset关联起来。
(7)shared_ptr:用于在类之间进行数据共享;
设计思想:
1.当我们设计一个类时,在真正实现成员之前先编写程序使用这个类,是一种非常有用的方法。通过这种方法,可以看到类是否具有我们所需要的操作。
2.在类的设计过程中,最重要的就是接口的设计,也就是头文件的设计。
注意的问题:
1.IO对象无拷贝或赋值,需要以引用的方式传递参数和返回流。
2.auto& lines = wm[word];这里也需要是按引用传递,否则会发生拷贝,不能操作map中的set,结果为空。
源代码与C++Primer(第5版)第十二章给出的稍有改动,我认为书中讲的统计单词出现次数的方法不合理。
1.main.cpp
#include "TxtQuery.h"
#include "queryResult.h"
#include "iostream"
void runQueries(std::ifstream& infile)
{
TxtQuery wordQuery(infile);
while (true)
{
std::cout<< "enter word to look for, or q to quit:";
std::string word;
if (!(std::cin >> word) || word == "q") break;
print(std::cout, wordQuery.query(word));
}
}
int main()
{
std::ifstream infile("1.txt");
runQueries(infile);
}
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <set>
#include <fstream>
#include "queryResult.h"
class TxtQuery
{
public:
TxtQuery();
~TxtQuery();
TxtQuery(std::ifstream& in);
queryResult query(const std::string& word);
private:
std::shared_ptr<std::vector<std::string>> file;
std::map<std::string, std::shared_ptr<std::multiset<int>>> wm;
};
#include "TxtQuery.h"
#include <sstream>
TxtQuery::TxtQuery()
{
}
TxtQuery::~TxtQuery()
{
}
TxtQuery::TxtQuery(std::ifstream& in) :file(new std::vector<std::string>)
{
std::string text;
while (std::getline(in, text))
{
file->push_back(text);
int n = file->size();
std::istringstream line(text);
std::string word;
while (line >> word)
{
auto& lines = wm[word];
if (!lines)
{
lines.reset(new std::multiset<int>);
}
lines->insert(n);
}
}
}
queryResult TxtQuery::query(const std::string& word)
{
static std::shared_ptr<std::multiset<int>> nodata(new std::multiset<int>);
auto loc = wm.find(word);
if(loc == wm.end())
{
return queryResult(word, nodata, file);
}
else
{
return queryResult(word, loc->second, file);
}
}
4、queryResult.h
#pragma once
#include <string>
#include <memory>
#include <set>
#include <vector>
class queryResult
{
public:
queryResult();
~queryResult();
queryResult(const std::string sought, std::shared_ptr<std::multiset<int>> lines, std::shared_ptr<std::vector<std::string>> file) :
sought(sought)
,lines(lines)
,file(file) {}
friend std::ostream& print(std::ostream&, const queryResult&);
private:
std::string sought;
std::shared_ptr<std::multiset<int>> lines;
std::shared_ptr<std::vector<std::string>> file;
};
#include "queryResult.h"
#include <iostream>
queryResult::queryResult()
{
}
queryResult::~queryResult()
{
}
std::ostream& print(std::ostream& os, const queryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size() << " " << "times" << std::endl;
//用multiset初始化set,重复的内容自动去除
std::set<int> single(std::begin(*qr.lines), end(*qr.lines));
for (auto num : single) {
os << "\t(line " << num << ")" << *(qr.file->begin() + num - 1) << std::endl;
}
std::cout << std::endl;
return os;
}