栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ Primer - TextQuery设计

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ Primer - TextQuery设计

        在C++ Primer书中设计了TextQuery的程序,该程序可以实现从本地加载文章,并记录每个单词位于第几行。当用户输入单词,程序就可以返回所有的包含该单词的一行文字。实验结果如下图所示:

        因此,我们可以简单分析一下,对于TextQuery类而言,它涉及到哪些数据结构?

(1)需要打印某一行的文字,因此我们可以使用vector将每一行的文字都进行保存。

(2)当用户输入某个单词时,程序就会打印包含该单词的所有行。因此,就涉及到map数据结果。键是单词,而值则是该单词在哪些行中出现了。而值则使用set进行表示,使得某一行出现多次的一个单词,仅仅打印一次该段的单词。

(3)为了分割一段文字中所有的单词,我们可以使用istringstream类,该类可以根据空格对一段文字进行分割。

        接着,我们需要分析一下TextQuery涉及到哪些成员方法,从而支持上述我们的需求呢?

(1)需要提供读取文件的方法:这个方法可以继承在TextQuery中构造函数中,一旦构造了TextQuery的实例对象之后,就已经实现了对某个文件的读取与加载。

(2)需要提供查询的方法:该方法支持我们输入单词,并输出包含该单词的所有行(重复的行文字仅仅打印一次)。

(3)打印最终的查询结果:我们可以再创建一个成员方法,实现对查询结果的打印。但是C++ Primer提供了一个耦合性更低的思路,额外创建一个QueryResult类的方法来代替在TextQuery中创建一个成员方法的思路。我们将最终的查询结果封装在QueryResult类中,使用其友元函数来打印最终的效果。

1. testQuery.hpp程序
#ifndef __TEXTQUERY_H__
#define __TEXTQUERY_H__

#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 对QueryResult类进行声明,因为TextQuery类的query函数需要返回QueryResult对象
class QueryResult;
class TextQuery{
private:
    using data_type = std::vector::size_type;
    std::shared_ptr> content;
    std::map>> record;
public:
    TextQuery(std::ifstream& is);
    QueryResult query(const std::string& word);
    ~TextQuery(){};
};
# endif

        如上述代码所示,我们首先声明了QueryResult类,这样在TextQuery类中就可以显示的定义query查询方法了。上述代码有几点是需要着重注意的:

(1)使用vector存放的每行文字,使用shared_ptr进行封装了;set存在某个单词在哪些行出现,也是用shared_ptr进行封装了。一方面这些数据都需要在QueryResult中进行存在,shared_ptr表示这些数据可以在两个类之间进行共享;另一方面,使用智能指针可以省去我们对指针进行分配与销毁的精力。

(2)using关键字可以代替typedef对数据类型进行重命名,使用size_type而不是显示定义set的数据类型,因为在实际过程中,文件可大可小,需要根据具体的文件进行分析,因此我们不能在一开始就定死了set中数据的类型。

2. testQuery.cpp程序
#include 
#include 
#include "textQuery.hpp"
#include "queryResult.hpp"

TextQuery::TextQuery(std::ifstream& is):content(new std::vector){
    // 读取文件,将每一行数据保存到content中,然后再将每行中的word保存到record词典中
    std::string line;
    while(std::getline(is, line)){
        int line_num = content->size();
        content->emplace_back(line);
        std::istringstream iss(line);
        std::string word;
        while(iss >> word){
            auto& line_set = record[word];
            if(!line_set){
                // line_set = std::make_shared>(new std::set);
                line_set.reset(new std::set);
            }
            line_set->insert(line_num);
        }
    }
}

// 查询函数
QueryResult TextQuery::query(const std::string& word){
    auto pos = record.find(word);
    static std::shared_ptr> nodata;
    if(pos == record.end()){
        return QueryResult(word, nodata, content);
    }else{
        return QueryResult(word, pos->second, content);
    }
        
}

        上述代码是TextQuery类具体的实现代码,其中有几点需要特别注意:

(1)TextQuery并没有文件地址的属性,而是使用ifstream来代替文件地址。

(2)使用getline()函数进行每一行数据的读取、使用istringstream类对一行数据中多个单词进行分割。

(3)map的插入也是一个非常有技巧的点,我刚开始想的是使用count()方法来判断某一个次是否在之前就出现了。如果出现了,直接插入在其对应的set中即可;如果没出现,就需要我们创建set指针。但是随着文章数据的增多,count()方法的负担会越来越大,这会极大的降低程序的效率。因此,C++ Primer中首先索引出某个单词对应的set智能指针,如果智能指针为空,则我们需要创建智能指针(使用智能指针的reset方法);如果不为空,我们直接在智能指针中插入数据(使用智能指针的insert方法)。更重要的一点是,这个智能指针必须是引用,否则将不会对set产生变换。

(4)在query中,我们创建了一个局部静态变量的智能指针来表示“文中不存在该单词”的情况。对于word单词的查询,我们可以使用map的find()方法,如果返回的位置已经位于map的尾部了,说明文中不存在该单词,并构造一个包含空的set的QueryResult类;否则就构建包含该单词的所有行号的QueryResult类。

3. queryResult.hpp程序
#ifndef __QUERYRESULT__H_
#define __QUERYRESULT__H_
#include 
#include 
#include 
#include 
#include 
#include 

class QueryResult{
private:
    using data_type = std::vector::size_type;
    const std::string word;
    std::shared_ptr> content;
    std::shared_ptr> line_set;

public:
    QueryResult(const std::string& w, std::shared_ptr> ls, 
        std::shared_ptr> c):word(w),line_set(ls),content(c){};
    ~QueryResult(){};
    // 定义一个友元函数即可,用于打印查询的结果
    friend std::ostream& show_result(std::ostream& os, const QueryResult& qr);


};

#endif // __QUERYRESULT__H_

        QueryResult类主要是对查询的结果进行打印的。因此,show_result函数则并不需要是QueryResult类的成员方法,只需要该类的友元方法即可,并对QueryResult类的属性记性打印。

4. queryResult.cpp程序
#include "queryResult.hpp"


std::ostream& show_result(std::ostream& os, const QueryResult& qr){
    // 用于打印最终的结果,如果qr中的line_set为空的话,说明没找到相应的元素
    if(!qr.line_set){
        os << "该文中没有" << qr.word << "关键字" << std::endl;
        return os;
    }
    os << "总共有" << qr.line_set->size() << "行出现了" << qr.word << "单词:"<< std::endl;
    for(auto ln: *qr.line_set){
        os << "(line " << ln << "): " << (*qr.content)[ln] << std::endl;
    }
    return os;
}

        在show_result()方法中,我们主要用于对ostream类的使用,从而打印我们所希望看到的结果。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/873136.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号