STL实践与分析

--容器的综合应用:文本查询程序

引言:

本章中最重点的实例。由于不须要用到multiset与multimap的内容。于是将这一小节提到了前面。通过这个实例程序,大师分析问题的智慧,大师的编程风格。大师对程序的控制能力,由此可见一斑。因此。我对这一小节的内容差点儿不做改动。或仅仅做非常小的更改(由于有些东西不同人有不同的理解)。搬出来。以供大家细致品读。

要求:

我们的程序将读取用户指定的随意文本文件,然后同意用户从该文件里查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。假设某单词在同一行中多次出现,程序将仅仅显示该行一次

行号按升序显示。

例子:

以本章的内容作为文件输入,然后查找单词“element”。输出的前几行应为:

element occurs 125 times
(line 62) element with a given key.
(line 64) second element with the same key.
(line 153) element |==| operator.
(line 250) the element type.
(line 398) corresponding element.

一、查询程序的设计

设计程序的一个良好习惯是首先将程序所涉及的操作先列出来,明白须要提供的操作有助于建立须要的数据结构和实现这些行为。

从需求出发,我们的程序须要支持下面任务:

1)它必须同意用户指明要处理的文件名称字。

程序将存储该文件的内容,以便输出每一个单词所在的原始行。

2)它必须将每一行分解为各个单词,并记录每一个单词所在的全部行。在输出行号时,应保证以升序输出,而且不反复。

3)对特定单词的查询将返回出现该单词的全部行的行号。

4)输出某单词所在的行文本时,程序必须能依据给定的行号从输入文件里获取对应的行。

1、数据结构

设计一个简单的TextQuery类来实现这个程序:

1)使用一个vector<string>对象来存储整个输入文件。输入文件的每一行是该
vector对象的一个元素。

因而,在希望输出某一行时,仅仅需以行号为下标获取该行所在的元素就可以。

2)将每一个单词所在的行号存储在一个set容器对象中。使用set就可确保每行仅仅有一个条目,并且行号将自己主动按升序排列。

3)使用一个map容器将每一个单词与一个set容器对象关联起来,该set容器对象记录此单词所在的行号。

2、操作

对于类还要求有良好的接口。然而,一个重要的设计策略首先要确定:查询函数需返回存储一组行号的set对象。这个返回类型应该怎样设计呢?

其实,查询的过程相当简单:使用下标訪问map对象获取关联的set对象就可以。唯一的问题是怎样返回所找到的set对象。安全的设计方案是返回该set对象的副本。但如此一来,就意味着要复制set中的每一个元素。假设处理的是一个相当庞大的文件,则复制set对象的代价会很昂贵。其它可行的方法包含:返回一个pair对象,存储一对指向set中元素的迭代器;或者返回set对象的
const引用。为简单起见,我们在这里採用返回副本的方法,但注意:假设在实际应用中复制代价太大,须要新考虑事实上现方法。

第一、第三和第四个任务是使用这个类的程序猿将运行的动作。第二个任务则是类的内部任务。将这四任务映射为类的成员函数,则类的接口需提供下列三个public函数:

1)read_file成员函数,其形參为一个ifstream&
类型对象。

该函数每次从文件里读入一行,并将它保存在vector容器中。

输入完成后,read_file
将创建关联每一个单词及其所在行号的map容器。

2)run_query成员函数,其形參为一个string类型对象,返回一个set对
象,该set对象包括出现该string对象的全部行的行号。

3)text_line成员函数,其形參为一个行号,返回输入文本中该行号相应的文本行。

不管 run_query还是
text_line都不会改动调用此函数的对象,因此,可将这两个操作定义为const成员函数。

为实现read_file功能,还需定义两个private函数来读取输入文本和创建map容器:

1)store_file函数读入文件,并将文件内容存储在vector容器对象中。

2)build_map函数将每一行分解为各个单词,创建map容器对象,同一时候记录每一个单词出现行号。

二、TextQuery

//in TextQuery.h
class TextQuery
{
public:
typedef std::vector<std::string>::size_type line_no;
void read_file(std::ifstream &is)
{
store_file(is);
build_map();
}
std::set<line_no> run_query(const std::string &) const;
std::string text_line(line_no) const; private:
void store_file(std::ifstream &);
void build_map(); std::vector<std::string> line_of_text;
std::map< std::string,std::set<line_no> > word_map;
};

三、TextQuery类的使用

1、主程序

//in main.cpp
/**记得加入
*#include "TextQuery.h" 类的定义
*#include "other.h" 其它函数的声明
*/
int main(int argc,char **argv)
{
ifstream inFile;
if (argc != 2 || !open_file(inFile,argv[1]))
{
cerr << "No input file!" << endl;
return EXIT_FAILURE;
} TextQuery tq;
tq.read_file(inFile);
while (true)
{
cout << "enter word to look for,or q/Q to quit: ";
string s;
cin >> s;
if (!cin || s == "q" || s == "Q")
break; set<TextQuery::line_no> locs = tq.run_query(s);
print_results(locs,s,tq);
}
return 0;
}

2、辅助函数

print_results函数、make_plural函数、open_file函数的定义:

//in other.cpp
/**记得加入
*#include "TextQuery.h" 类的定义
*#include "other.h" 其它函数的声明
*/
string make_plural(set<TextQuery::line_no size,const string &begin,const string &ends);
ifstream &open_file(ifstream &in,const string &file)
{
in.close();
in.clear();
in.open(file.c_str()); return in;
} void print_results(set<TextQuery::line_no> &locs,
const string &sought,
const TextQuery &file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::size_type size = locs.size();
cout << "\n" << sought << " occurs "
<< size << " "
<< make_plural(size,"time","s") << endl; line_nums::iterator iter = locs.begin();
while (iter != locs.end())
{
cout << "\t(line "
<< (*iter) + 1 << ")"
<< file.text_line(*iter) << endl;
}
} string make_plural(set<TextQuery::line_no size,
const string &begin,
const string &ends)
{
return (size <= 1 ? begin : begin + ends);
}

四、编写成员函数[inTextQuery.h]

1、存储输入文件

void TextQuery::store_file(ifstream &is)
{
string textline;
while (getline(is,textline))
{
line_of_text.push_back(textline);
}
}

2、建立单词map容器

void TextQuery::build_map()
{
for (line_no line_num = 0;
line_num != line_of_text.size();
++line_num)
{
istringstream line(line_of_text[line_num]);
string word; while (line >> word)
{
word_map[word].insert(line_num);
}
}
}

【分析:】

	word_map[word].insert(line_num);

将word用做
map容器的下标。假设word在
word_map容器对象中不存在,那么下标操作符将该word加入到此容器中,并将其关联的值初始化为空的set。

无论是否加入了word,下标运算都返回一个set对象,然后调用insert函数在该
set对象中加入当前行号。假设某个单词在同一行中反复出现,那么insert函数的调用将不做不论什么操作。

3、支持查询

set<TextQuery::line_no>
TextQuery::run_query(const std::string &query_word) const
{
map<string,set<line_no> >::const_iterator
loc = word_map.find(query_word); if (loc == word_map.end())
{
return set<TextQuery::line_no>();
}
else
{
return loc -> second;
}
}

run_query函数带有指向conststring
类型对象的引用參数,并以这个參数作为下标来訪问word_map对象。

如果成功找到这个string,那么该函数返回关联此string的
set对象,否则返回一个空的set对象。

4、run_query返回值的使用

string TextQuery::text_line(line_no line) const
{
if (line < line_of_text.size())
{
return line_of_text[line];
}
throw out_of_range("line number out of range");
}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

C++ Primer 学习笔记_38_STL实践与分析(12)--集成的应用程序容器:文本查询程序的更多相关文章

  1. C++ Primer 学习笔记_32_STL实践与分析(6) --再谈string类型(下)

    STL实践与分析 --再谈string类型(下) 四.string类型的查找操作 string类型提供了6种查找函数,每种函数以不同形式的find命名.这些操作所有返回string::size_typ ...

  2. C++ Primer 学习笔记_35_STL实践与分析(9)--map种类(在)

    STL实践与分析 --map类型(上) 引: map是键-值对的集合. map类型通常能够理解为关联数组:能够通过使用键作为下标来获取一个值,正如内置数组类型一样:而关联的本质在于元素的值与某个特定的 ...

  3. C++ Primer 学习笔记_43_STL实践与分析(17)--再谈迭代器【中】

    STL实践与分析 --再谈迭代器[中] 二.iostream迭代[续] 3.ostream_iterator对象和ostream_iterator对象的使用 能够使用ostream_iterator对 ...

  4. C++ Primer 学习笔记_46_STL实践与分析(20)--容器特有的算法

    STL实践与分析 --容器特有的算法 与其它顺序容器所支持的操作相比,标准库为list容器定义了更精细的操作集合,使它不必仅仅依赖于泛型操作.当中非常大的一个原因就是list容器不是依照内存中的顺序进 ...

  5. C++ Primer 学习笔记_44_STL实践与分析(18)--再谈迭代器【下】

    STL实践与分析 --再谈迭代器[下] 三.反向迭代器[续:习题] //P355 习题11.19 int main() { vector<int> iVec; for (vector< ...

  6. C++ Primer 学习笔记_40_STL实践与分析(14)--概要、先来看看算法【上】

    STL实践与分析 --概述.初窥算法[上]     标准库容器定义的操作很少.并没有给容器加入大量的功能函数.而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是"泛型"的. ...

  7. C++ Primer 学习笔记_45_STL实践与分析(19)--建筑常规算法

    STL实践与分析 --泛型算法的结构 引言: 正如全部的容器都建立在一致的设计模式上一样,算法也具有共同的设计基础. 算法最主要的性质是须要使用的迭代器种类.全部算法都指定了它的每一个迭代器形參可使用 ...

  8. C++ Primer 学习笔记_33_STL实践与分析(7) --容器适配器

    STL实践与分析 --容器适配器 引: 除了顺序容器.标准库还提供了三种顺序容器适配器:queue,priority_queue和stack.适配器是标准库中的概念.包含容器适配器,迭代器适配器和函数 ...

  9. C++ Primer 学习笔记_41_STL实践与分析(15)--先来看看算法【下一个】

    STL实践与分析 --初窥算法[下] 一.写容器元素的算法 一些算法写入元素值.在使用这些算法写元素时一定要当心.必须.写入输入序列的元素 写入到输入序列的算法本质上是安全的--仅仅会写入与指定输入范 ...

随机推荐

  1. 新型I/O架构引领存储之变(四)

    新型I/O架构引领存储之变(四) 作者:廖恒 应对挑战--商务及技术考量 本文前面的部分分析了砖块模式与生俱来的总拥有成本(TCO)过高的问题.为了战胜这一挑战,超大规模数据中心的运营者须要从两个不同 ...

  2. HTML contact form with CAPTCHA

    http://www.html-form-guide.com/contact-form/html-contact-form-captcha.html#codedownload

  3. [WebView五学习]:调试Web Apps

    上一篇我们学习了([WebView学习之四]:迁移到Android4.4版本号的WebView),今天我们来继续学习. (博客地址:http://blog.csdn.net/developer_jia ...

  4. 为什么android你用的越多,速度较慢的手机

    依据第三方的调研数据显示,有 77% 的 Android 手机用户承认自己曾遭遇过手机变慢的影响,百度搜索"Android+ 卡慢",也有超过 460 万条结果. 在业内.Andr ...

  5. hdu1978--How many ways(内存搜索)

    How many ways Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  6. iOS Foundation 框架基类

    iOS Foundation 框架基类 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转 ...

  7. cocos2d之Box2D详细说明 鼠标联合实现

    cocos2d之Box2D具体解释 鼠标关节实现 DionysosLai2014-5-7 我们常常要移动物理世界中的某个物体,例如说石头.木块等.假设我们直接改变这些物体的位置,让这些物体尾随我们手指 ...

  8. Java OCR tesseract 图像智能字符识别技术

    公司有需求啊,所以就得研究哈,最近公司需要读验证码,于是就研究起了图像识别,应该就是传说中的(OCR:光学字符识别OCR),下面把今天的收获整理一个给大家做个分享. 本人程序用的tesseract,官 ...

  9. CentOS 6.3 安装 samba 共享(转)

    PHP环境在linux下,但是开发的时候用的是windows,于是我用了samba将linux的一个目录共享,然后在windows上做映射,这样就可以直接在windows下编辑linux上的文件了 首 ...

  10. V微软S2015下载:开展Win10/Linux/iOS多平台软件

    微软VS2015下载:开展Win10/Linux/iOS多平台软件 资源:IT之家作者:子非         责任编辑:子非   11月13日消息,微软刚刚宣布了 Visual Studio 2015 ...