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

《C++ Primer》第12章 12.3节习题答案

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

《C++ Primer》第12章 12.3节习题答案

《C++ Primer》第12章 动态内存

12.3节使用标准库:文本查询程序 习题答案

练习12.27:TextQuery和QueryResult类只使用了我们已经介绍过的语言和标准库特性。不要提前看后续章节内容,只用已经学到的知识对这两个类编写你自己的版本。

【出题思路】

本题综合练习已学过的知识实现文本查询程序。

【解答】

#ifndef PROGRAM12_27_H
#define PROGRAM12_27_H

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

using std::shared_ptr;
using std::vector;
using std::string;
using std::ifstream;

class QueryResult;
class TextQuery
{
public:
    using LineNo = vector::size_type;

    TextQuery(ifstream &);
    QueryResult query(const string &) const;

private:
    shared_ptr> input;
    std::map>> result;
};

class QueryResult
{
public:
    friend std::ostream& print(std::ostream&, const QueryResult&);

public:
    QueryResult(const string& s, shared_ptr> set, shared_ptr> v)
    :word(s), nos(set), input(v)
    {

    }

private:
    string word;
    shared_ptr> nos;
    shared_ptr> input;
};

TextQuery::TextQuery(std::ifstream& ifs):input(new vector)
{
    LineNo lineNo{0};
    for(string line; std::getline(ifs, line); ++lineNo)
    {
        input->push_back(line);
        std::istringstream line_stream(line);
        for(string text, word; line_stream >> text; word.clear())
        {
            //避免读一个单词后跟标点符号(如:word,)
            std::remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct);
            //use reference avoid count of shared_ptr add.
            auto &nos = result[word];
            if(!nos) nos.reset(new std::set);
            nos->insert(lineNo);
        }
    }
}


QueryResult TextQuery::query(const string& str) const
{
    //使用静态只分配一次。
    static shared_ptr> nodate(new std::set);
    auto found = result.find(str);
    if(found == result.end())
    {
        return QueryResult(str, nodate, input);
    }
    else
    {
        return QueryResult(str, found->second, input);
    }
}

std::ostream& print(std::ostream& out, const QueryResult& qr)
{
    out << qr.word << "  occurs  " << qr.nos->size()
    << (qr.nos->size() > 1? " times" : " time") << std::endl;
    for(auto i : *qr.nos)
    {
        out << "t(line " << i + 1 << ") " << qr.input->at(i) << std::endl;
    }
    return out;
}

#endif // PROGRAM12_27_H
#include 
#include "program12_27.h"

using std::cout;
using std::endl;

void runQueries(std::ifstream& infile)
{
    TextQuery tq(infile);
    while(true)
    {
        std::cout << "enter word to lock for, or q to quit:" << endl;
        string s;
        if(!(std::cin >> s) || (s == "q"))
            break;
        print(std::cout, tq.query(s)) << std::endl;
    }
}


int main(int argc, const char* argv[])
{
    std::ifstream in(argv[1]);
    if(!in)
    {
        cout << "无法打开输入文件" << endl;
        return -1;
    }
    runQueries(in);

    return 0;
}

data12_27.txt文件内容为:

Alice Emma has long flowing red hair.
Her Daddy says when the wind blows
through her hair, it looks almost alive,
like a fiery bird in flight.
A beautiful fiery bird, he tells her,
magical but untamed.
"Daddy, shush, there is no such thing,"
she tells him, at the same time wanting
him to tell her more.
Shyly, she asks, "I mean, Daddy, is there?"

设置命令行参数:

 运行结果:

 练习12.28:编写程序实现文本查询,不要定义类来管理数据。你的程序应该接受一个文件,并与用户交互来查询单词。使用vector、map和set容器保存来自文件的数据并生成查询结果。

【出题思路】

采用过程式程序设计而非面向对象的程序设计来解决这个问题,并体会两者的差异。

【解答】

总体设计思路与面向对象的版本相似,但有一些差异:

1.由于不用类来管理数据,file和wm都定义为全局变量,便于在函数间共享。当然也可以定义为局部变量,通过函数参数传递。

2.由于不必进行不同类对象间的数据共享,因此file和wm中的set都不必用shared_ptr管理,直接定义为vector和set即可。使用它们的代码也要相应修改。

3.由于不用类来保存查询结果,因此将query和print函数合二为一。

#include 
using std::size_t;

#include 
using std::string;

#include 
using std::cout;
using std::endl;

#ifndef MAKE_PLURAL_H
#define MAKE_PLURAL_H

// return the plural version of word if ctr is greater than 1
inline
string make_plural(size_t ctr, const string &word,
                               const string &ending)
{
    return (ctr > 1) ? word + ending : word;
}

#endif // MAKE_PLURAL_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  //要使用EXIT_FAILURE

#include "make_plural.h"

using namespace std;

using line_no = vector::size_type;
vector file;            //文件每行内容
map> 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);                   //保存此行文本
        unsigned long n = file.size() - 1;      //当前行号
        istringstream line(text);               //将行文本分解为单词
        string word;
        while(line >> word)                     //对行中每个单词
        {
            //将当前行号插入到其行中与set中,如果单词不在wm中,以之为下标在vm中添加一项
            wm[cleanup_str(word)].insert(n);
        }
    }
}


ostream &query_and_print(const string &sought, ostream &os)
{
    //使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
    auto loc = wm.find(sought);
    if(loc == wm.end())             //未找到
    {
        os << sought << "出现了0次" << endl;
    }
    else
    {
        auto lines = loc->second;   //行号set
        os << sought << "出现了" << lines.size() << "次" << endl;
        for(auto num: lines)        //打印单词出现的每一行
        {
            os << "t(第" << num + 1 << "行)" << *(file.begin() + num) << endl;
        }
    }
    return os;
}

void runQueries(ifstream &infile)
{
    //infile是一个ifstream,指向我们要查询的文件
    input_text(infile);//读入文本并建立查询map
    //与用户交素养:提示用户输入要查询的单词,完成查询并打印结果
    while(true)
    {
        cout << "enter word to look for, or q to quit:" << endl;
        string s;
        //若遇到文件尾或用户输入了q时循环终止
        if(!(cin >> s) || s == "q")
            break;
        //指向查询并打印结果
        query_and_print(s, cout) << endl;
    }
}

//程序接受唯一的命令行参数,表示文件文件名
int main(int argc, const char * argv[])
{
    //打开要查询的文件
    ifstream infile;
    //打开文件失败败,程序异常退出
    if(argc < 2 || !(infile.open(argv[1]), infile))
    {
        cerr << "No input file!" << endl;
        return EXIT_FAILURE;
    }
    runQueries(infile);
    cout << "hello world" << endl;
    return 0;
}

使用上题的数据文件data12_27.txt文件,设置命令行参数,运行结果如下:

 

练习12.29:我们曾经用do while循环来编写管理用户交互的循环(参见5.4.4节,第169页)。用do while重写本节程序,解释你倾向于哪个版本,为什么。

【出题思路】

采用过程式程序设计而非面向对象的程序设计来解决这个问题,并体会两者的差异。

【解答】

循环改成如下形式即可:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   //要使用EXIT_FAILURE

using namespace std;

using line_no = vector::size_type;
vector file;//文件每行内容
map> 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);                   //保存此行文本
        unsigned long n = file.size() - 1;      //当前行号
        istringstream line(text);               //将行文本分解为单词
        string word;
        while(line >> word)                     //对行中每个单词
        {
            //将当前行号插入到其行中与set中,如果单词不在wm中,以之为下标在vm中添加一项
            wm[cleanup_str(word)].insert(n);
        }
    }
}

ostream &query_and_print(const string &sought, ostream &os)
{
    //使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
    auto loc = wm.find(sought);
    if(loc == wm.end())             //未找到
    {
        os << sought << "出现了0次" << endl;
    }
    else
    {
        auto lines = loc->second;   //行号set
        os << sought << "出现了" << lines.size() << "次" << endl;
        for(auto num: lines)        //打印单词出现的每一行
        {
            os << "t(第" << num + 1 << "行)" << *(file.begin() + num) << endl;
        }
    }
    return os;
}

void runQueries(ifstream &infile)
{
    //infile是一个ifstream,指向我们要查询的文件
    input_text(infile);//读入文本并建立查询map
    //与用户交素养:提示用户输入要查询的单词,完成查询并打印结果
    do//显示,由于循环中的执行步骤是“输入——检查循环条件——执行查询”,检查循环条件是中间步骤,因此,while和do while没有什么差别,不会比另一个更简洁
    {
        cout << "enter word to look for, or q to quit:" << endl;
        string s;
        //若遇到文件尾或用户输入了q时循环终止
        if(!(cin >> s) || s == "q")
            break;
        //指向查询并打印结果
        query_and_print(s, cout) << endl;
    }while(true);
}


//程序接受唯一的命令行参数,表示文件文件名
int main(int argc, const char * argv[])
{
    //打开要查询的文件
    ifstream infile;
    //打开文件失败败,程序异常退出
    if(argc < 2 || !(infile.open(argv[1]), infile))
    {
        cerr << "No input file!" << endl;
        return EXIT_FAILURE;
    }
    runQueries(infile);
    cout << "hello world" << endl;
    return 0;
}

设置命令行参数,运行结果如下:

 

练习12.30:定义你自己版本的TextQuery和QueryResult类,并执行12.3.1节(第431页)中的runQueries函数。

【出题思路】

本题综合练习已学过的知识实现文本查询程序。

【解答】

解答同练习12.27。

练习12.31:如果用vector代替set保存行号,会有什么差别?哪种方法更好?为什么?

【出题思路】

理解vector和set的差异。

【解答】

对这个问题而言,vector更好。因为,虽然vector不会维护元素值的序,set会维护关键字的序,但注意到,我们是逐行读取输入文本的,因此每个单词出现的行号是自然按升序加入到容器中的,不必特意用关联容器来保证行号的升序。而从性能角度,set是基于红黑树实现的,插入操作时间复杂性为O(logn)(n为容器中元素数目),而vector的push_back可达到常量时间。另外,一个单词在同一行中可能出现多次。set自然可保证关键字不重复,但对vector这也不成为障碍——每次添加行号前与最后一个行号比较一下即可。总体性能仍然是vector更优。

练习12.32:重写TextQuery和QueryResult类,用StrBlob代替vector保存输入文件。

【出题思路】

本题练习在较大程序中配合使用StrBlob和StrBlobPtr来代替用shared_ptr管理的vector及迭代器。

【解答】

对my_QueryResult.h、my_TextQuery.h和my_StrBlob.h

相对于书中的原程序进行如下修改:

1.在my_QueryResult.h中包含头文件my_StrBlob.h。

2.QueryResult类的file成员改为StrBlob类型,相应的,构造函数的第三个参数和成员函数get_file的返回类型也都改为StrBlob类型。

3.类似的TextQuery类的成员file也改为StrBlob类型。

4.由于file不再是shared_ptr而是StrBlob,TextQuery构造函数(my_TextQuery.cpp)中的“file->”均改为“file.”。

5.在原来的代码中,TextQuery构造函数动态分配了一个vector,用其指针初始化file成员(shared_ptr)。但StrBlob类并未定义接受vector *的构造函数,因此我们在my_StrBlob.h文件中为其添加了这个构造函数,用指针参数直接初始化data成员(shared_ptr)。

6.在函数print(my_TextQuery.cpp)中,用file->begin()获得了vector的首位置迭代器,对其进行加法操作获得了指向第num个string的迭代器,最后通过解引用获得了这个string,将其打印出来。但StrBlobPtr只定义递增和递减操作,并未定义加法运算。因此,我们为其增加了my_StrBlob.h接受一个整型参数off的deref操作,能解引用出距当前位置curr偏移量为off的元素(但并不会修改curr的值)。

至此,所需要的修改进行完毕。

可以看到,我们对使用文本查询类的主程序未进行任何修改!读者可好好体会面向对象程序设计将接口和实现分离的优点。

#include 
using std::size_t;

#include 
using std::string;

#include 
using std::cout;
using std::endl;

#ifndef MAKE_PLURAL_H
#define MAKE_PLURAL_H

// return the plural version of word if ctr is greater than 1
inline
string make_plural(size_t ctr, const string &word,
                               const string &ending)
{
    return (ctr > 1) ? word + ending : word;
}

#endif // MAKE_PLURAL_H
#ifndef SYSTRBLOB_32_H
#define SYSTRBLOB_32_H
#include 
#include 
#include 
#include 
#include 

using namespace std;

//提前声明,StrBlob中的友类声明所需
class StrBlobPtr;

class StrBlob
{
    friend class StrBlobPtr;
public:
    typedef vector::size_type size_type;
    StrBlob();
    StrBlob(initializer_list i1);
    StrBlob(vector *p);
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    //添加和删除元素
    void push_back(const string &t) { data->push_back(t); }
    void pop_back();
    //元素访问
    string& front();
    const string& front() const;
    string& back();
    const string& back() const;

    //提供给StrBlobPtr的接口
    StrBlobPtr begin(); //定义StrBlobPtr后才能定义这两个函数
    StrBlobPtr end();
    //const版本
    StrBlobPtr begin() const;
    StrBlobPtr end() const;

private:
    shared_ptr> data;
    //如果data[i]不合法,抛出一个异常
    void check(size_type i, const std::string &msg) const;
};

inline StrBlob::StrBlob(): data(make_shared>())
{

}

inline StrBlob::StrBlob(initializer_list i1)
        :data(make_shared>(i1))
{

}

inline StrBlob::StrBlob(vector *p)
        :data(p)
{

}

inline void StrBlob::check(size_type i, const string &msg) const
{
    if(i >= data->size())
        throw out_of_range(msg);
}


inline string& StrBlob::front()
{
    //如果vector为空,check会抛出一个异常
    check(0, "front on empty StrBlob");
    return data->front();
}

//const版本front
inline const string& StrBlob::front() const
{
    check(0, "front on empty StrBlob");
    return data->front();
}

inline string& StrBlob::back()
{
    check(0, "back on empty StrBlob");
    return data->back();
}

//const版本back
inline const string& StrBlob::back() const
{
    check(0, "back on empty StrBlob");
    return data->back();
}

inline void StrBlob::pop_back()
{
    check(0, "pop_back on empty StrBlob");
    data->pop_back();
}

//当试图访问一个不存在的元素时,StrBlobPtr抛出一个异常
class StrBlobPtr
{
    friend bool eq(const StrBlobPtr&, const StrBlobPtr&);
public:
    StrBlobPtr()
    :curr(0)
    {

    }

    StrBlobPtr(StrBlob &a, size_t sz = 0)
    :wptr(a.data), curr(sz)
    {

    }
    StrBlobPtr(const StrBlob &a, size_t sz = 0)
    :wptr(a.data), curr(sz)
    {

    }

    string& deref() const;
    string& deref(int off) const;
    StrBlobPtr& incr();//前缀递增
    StrBlobPtr& decr();//前缀递减

private:
    //若检查成功,check返回一个指向vector的shared_ptr
    shared_ptr> check(size_t, const string&) const;
    //保存一个weak_ptr,意味着底层vector可能会被销毁
    weak_ptr> wptr;
    size_t curr;//在数组中的当前位置
};

inline shared_ptr> StrBlobPtr::check(size_t i, const string &msg) const
{
    auto ret = wptr.lock();//vector还存在吗?
    if(!ret)
    {
        throw runtime_error("unbound StrBlobPtr");
    }
    if(i >= ret->size())
    {
        throw out_of_range(msg);
    }
    return ret;//否则,返回指向vector的shared_ptr;
}

inline string& StrBlobPtr::deref() const
{
    auto p = check(curr, "dereference past end");
    return (*p)[curr]; //(*p)是对象所指向的vector
}

inline string& StrBlobPtr::deref(int off) const
{
    auto p = check(curr + off, "dereference past end");
    return (*p)[curr + off];//(*p)是对象所指向的vector
}

//前缀递增:返回递增后的对象的引用
inline StrBlobPtr& StrBlobPtr::incr()
{
    //如果curr已经指向容器的尾后位置,就不能递增它
    check(curr, "increment past end of StrBlobPtr");
    ++curr;//推进当前位置
    return *this;
}

//前缀递减:返回递减后的对象的引用
inline StrBlobPtr& StrBlobPtr::decr()
{
    //如果curr已经为0, 递减它就会产生一个非法下标
    --curr;//递减当前位置
    check(-1, "decrement past begin of StrBlobPtr");
    return *this;
}

//StrBlob的begin和end成员的定义
inline StrBlobPtr StrBlob::begin()
{
    return StrBlobPtr(*this);
}

inline StrBlobPtr StrBlob::end()
{
    auto ret = StrBlobPtr(*this, data->size());
    return ret;
}

//const版本
inline StrBlobPtr StrBlob::begin() const
{
    return StrBlobPtr(*this);
}

inline StrBlobPtr StrBlob::end() const
{
    auto ret = StrBlobPtr(*this, data->size());
    return ret;
}

//StrBlobPtr的比较操作
inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{
    auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
    //若底层的vector是同一个
    if(l == r)
    {
        //则两个指针都是空,或都是指向相同元素时,它们相等
        return (!r || lhs.curr == rhs.curr);
    }
    else
    {
        return false;//若指向不同vector,则不可能相等
    }
}

inline bool neq(const StrBlobPtr lhs, const StrBlobPtr &rhs)
{
    return !eq(lhs, rhs);
}

#endif // SYSTRBLOB_32_H
#ifndef QUERYRESULT_32_H
#define QUERYRESULT_32_H

#include 
#include 
#include 
#include 
#include 
#include "SYStrBlob_32.h"

class QueryResult
{
    friend std::ostream& print(std::ostream&, const QueryResult&);
public:
    typedef std::vector::size_type line_no;
    typedef std::set::const_iterator line_it;
    QueryResult(std::string s, std::shared_ptr> p, StrBlob f)
    :sought(s), lines(p), file(f)
    {

    }

    std::set::size_type size() const
    {
        return lines->size();
    }

    line_it begin() const
    {
        return lines->cbegin();
    }

    line_it end() const
    {
        return lines->cend();
    }

    StrBlob get_file()
    {
        return file;
    }

private:
    std::string sought;//要查询的单词
    std::shared_ptr> lines;//单词出现的行号的集号
    StrBlob file;//输入文件
};

std::ostream &print(std::ostream&, const QueryResult&);

#endif // QUERYRESULT_32_H
#ifndef TEXTQUERY_32_H
#define TEXTQUERY_32_H

#include 
#include 
#include 
#include 
#include 
#include 
#include "QueryResult_32.h"


class QueryResult;//这个声明类是必须的,查询函数需返回QueryResult类型
class TextQuery
{
public:
    using line_no = std::vector::size_type;
    TextQuery(std::ifstream&);
    QueryResult query(const std::string&) const;
    void display_map();//调试辅助函数;打印映射表
private:
    StrBlob file;//输入文件
    //将每个单词映射到它所出现的行号的集合
    std::map>> wm;
    //规范文本:删除标点,并转换为小写
    static std::string cleanup_str(const std::string&);
};

#endif // TEXTQUERY_32_H
#include "TextQuery_32.h"
#include "make_plural.h"

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

using std::size_t;
using std::shared_ptr;
using std::istringstream;
using std::string;
using std::getline;
using std::vector;
using std::map;
using std::set;
using std::cerr;
using std::cout;
using std::cin;
using std::ostream;
using std::endl;
using std::ifstream;
using std::ispunct;
using std::tolower;
using std::strlen;
using std::pair;

//读取输入文件,建立映射
TextQuery::TextQuery(ifstream &is)
    :file(new vector)
{
    string text;
    while(getline(is, text))//读取文件的每一行
    {
        file.push_back(text);//保存读入的文本行
        unsigned long n = file.size() - 1;//当前行号
        istringstream line(text);//从行中分离出单词
        string word;
        while(line >> word)//对行中的每个单词
        {
            word = cleanup_str(word);
            //如果单词还未在wm中,使用下标操作将其添加进去
            auto &lines = wm[word];//lines 是一个shared_ptr
            if(!lines)
            {
                lines.reset(new set); //分配一个新的set
            }
            lines->insert(n);//插入当前行号
        }
    }
}

//cleanup_str删除标点同,并将所有文本转换为小写形式,从而查询是大小写不敏感的
string TextQuery::cleanup_str(const string &word)
{
    string ret;
    for(auto it = word.begin(); it != word.end(); ++it)
    {
        if(!ispunct(*it))
        {
            ret += tolower(*it);
        }
    }
    return ret;
}

QueryResult TextQuery::query(const string &sought) const
{
    //如果未找到sought,将返回一个指向下面这个set的指针
    static shared_ptr> nodata(new set);
    //使用fine而不是下标操作的原因是避免将不在wm中的单词添加进去!
    auto loc = wm.find(cleanup_str(sought));
    if(loc == wm.end())
    {
        return QueryResult(sought, nodata, file);//未找到
    }
    else
    {
        return QueryResult(sought, loc->second, file);
    }
}

ostream &print(ostream &os, const QueryResult &qr)
{
    //如果找到了单词,打印出现次数及所有出现的行号
    os << qr.sought << " occurs " << qr.lines->size() << " " << make_plural(qr.lines->size(), "time", "s") << endl;
    //打印单词出现的每一行
    for(auto num: *qr.lines)//对set中每个元素
    {
        //不让用户对从0开始的文件本行号困惑
        os << "t(line " << num + 1 << ") " << qr.file.begin().deref(num) << endl;
    }
    return os;
}

//调用函数
void TextQuery::display_map()
{
    auto iter = wm.cbegin(), iter_end = wm.cend();
    //对map中的每个单词
    for(; iter != iter_end; ++iter)
    {
        cout << "word: " << iter->first << " {";
        //以常量引用方式获取位置向量,避免拷贝
        auto text_locs = iter->second;
        auto loc_iter = text_locs->cbegin();
        auto loc_iter_end = text_locs->cend();
        //打印此单词出现的所有行号
        while(loc_iter != loc_iter_end)
        {
            cout << *loc_iter;
            if(++loc_iter != loc_iter_end)
                cout << ", ";
        }
        cout << "}t"; //此单词的输出列表结束
    }
    cout << endl;//结束整个map的输出
}
#include "TextQuery_32.h"
#include "make_plural.h"
#include 
#include 
#include 
#include //包含EXIT_FAILURE的定义


using std::string;
using std::ifstream;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;


void runQueries(ifstream &infile)
{
    //infile是一个ifstream,指向我们要查询的文件
    TextQuery tq(infile);//保存文件并创建映射表
    //程序主循环,提示用户输入一个单词,查询此单词并打印结果
    while(true)
    {
        cout << "enter word to look for, or q to quit:" << endl;
        string s;
        //若遇到文件尾或用户输入了q时循环终止
        if(!(cin >> s) || s == "q")
            break;
        //执行查询并打印结果
        print(cout, tq.query(s)) << endl;
    }
}


//程序接受唯一的命令行参数,表示文件文件名
int main(int argc, const char * argv[])
{
    //打开要查询的文件
    ifstream infile;
    //打开文件失败败,程序异常退出
    if(argc < 2 || !(infile.open(argv[1]), infile))
    {
        cerr << "No input file!" << endl;
        return EXIT_FAILURE;
    }
    runQueries(infile);
    cout << "hello world" << endl;
    return 0;
}

设置命令行参数,运行结果如下:

 

练习12.33:在第15章中我们将扩展查询系统,在QueryResult类中将会需要一些额外的成员。添加名为begin和end的成员,返回一个迭代器,指向一个给定查询返回的行号的set中的位置。再添加一个名为get_file的成员,返回一个shared_ptr,指向QueryResult对象中的文件。

【出题思路】

本题练习在较大的类中添加迭代器等功能。

【解答】

对于begin和end成员,希望返回行号set中的位置,因此直接调用lines的cbegin和cend即可。对于get_file,直接返回file成员即可。

#include 
using std::size_t;

#include 
using std::string;

#include 
using std::cout;
using std::endl;

#ifndef MAKE_PLURAL_H
#define MAKE_PLURAL_H

// return the plural version of word if ctr is greater than 1
inline
string make_plural(size_t ctr, const string &word,
                               const string &ending)
{
    return (ctr > 1) ? word + ending : word;
}

#endif // MAKE_PLURAL_H
#ifndef PROGRAM12_33_H
#define PROGRAM12_33_H


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


class QueryResult;//declaration needed for return type in the query function
class TextQuery
{
public:
    using line_no = std::vector::size_type;
    TextQuery(std::ifstream&);
    QueryResult query(const std::string&) const;
    void display_map();//debugging aid: print the map

private:
    std::shared_ptr> file;//输入文件
    //maps each word to the set of the lines in which that word appears
    std::map>> wm;
    //canonicalizes text: removes punctuation and makes everything lower case
    static std::string cleanup_str(const std::string&);
};


class QueryResult
{
    friend std::ostream& print(std::ostream&, const QueryResult&);
public:
    typedef std::vector::size_type line_no;
    typedef std::set::const_iterator line_it;
    QueryResult(std::string s, std::shared_ptr> p, std::shared_ptr> f)
    :sought(s), lines(p), file(f)
    {

    }
    line_it begin() const { return lines->cbegin(); }
    line_it end() const { return lines->cend(); }
    std::shared_ptr> get_file() { return file; }

private:
    std::string sought;//word this query represents
    std::shared_ptr> lines;//lines it's on
    std::shared_ptr> file;//input file
};


std::ostream &print(std::ostream&, const QueryResult&);

#endif // PROGRAM12_33_H
#include   //istringstream
#include "program12_33.h"
#include "make_plural.h"
#include  //for EXIT_FAILURE


using std::size_t;
using std::shared_ptr;
using std::istringstream;
using std::string;
using std::getline;
using std::vector;
using std::map;
using std::set;
using std::cerr;
using std::cout;
using std::cin;
using std::ostream;
using std::endl;
using std::ifstream;
//using std::ispunct;
//using std::tolower;
using std::strlen;
using std::pair;


// read the input file and build the map of lines to line numbers
TextQuery::TextQuery(ifstream &is): file(new vector)
{
    string text;
    while(getline(is, text)) // for each line in the file
    {
        file->push_back(text);// remember this line of text
        unsigned long n = file->size() - 1;// the current line number
        istringstream line(text); // separate the line into words
        string word;
        while(line >> word)
        {
            word = cleanup_str(word);
            //if word isn't already in wm, subscripting adds a new entry
            auto &lines = wm[word];//lines is a shared_ptr
            if(!lines)//that pointer is null the first time we see word
            {
                lines.reset(new set);// allocate a new set
            }
            lines->insert(n);// insert this line number
        }
    }
}


// not covered in the book -- cleanup_str removes
// punctuation and converts all text to lowercase so that
// the queries operate in a case insensitive manner
string TextQuery::cleanup_str(const string &word)
{
    string ret;
    for(auto it = word.begin(); it != word.end(); ++it)
    {
        if(!ispunct(*it))
        {
            ret += tolower(*it);
        }
    }
    return ret;
}


QueryResult TextQuery::query(const string &sought) const
{
    // we'll return a pointer to this set if we don't find sought
    static shared_ptr> nodata(new set);
    //use find and not a subscript to avoid adding words to wm!
    auto loc = wm.find(cleanup_str(sought));
    if(loc == wm.end())
    {
        return QueryResult(sought, nodata, file);
    }
    else
    {
        return QueryResult(sought, loc->second, file);
    }
}


ostream &print(ostream &os, const QueryResult &qr)
{
    //if the word was found, print the count and all occurrences
    os << qr.sought << " occurs " << qr.lines->size() << " " << make_plural(qr.lines->size(), "time", "s") << endl;
    // print each line in which the word appeared
    for(auto num: *qr.lines)
    {
        // don't confound the user with text lines starting at 0
        os << "t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
    }
    return os;
}


// debugging routine, not covered in the book
void TextQuery::display_map()
{
    auto iter = wm.cbegin(), iter_end = wm.cend();
    //for each word in the map
    for(; iter != iter_end; ++iter)
    {
        cout << "word: " << iter->first << " {";
        // fetch location vector as a const reference to avoid copying it
        auto text_locs = iter->second;
        auto loc_iter = text_locs->cbegin(), loc_iter_end = text_locs->cend();
        // print all line numbers for this word
        while(loc_iter != loc_iter_end)
        {
            cout << *loc_iter;
            if(++loc_iter != loc_iter_end)
                cout << ", ";
        }
        cout << "}n";// end list of output this word
    }
    cout << endl; // finished printing entire map
}


void runQueries(ifstream &infile)
{
    //infile is an ifstream that is the file we want to query
    TextQuery tq(infile); // store the file and build the query map
    // iterate with the user: prompt for a word to find and print results
    while(true)
    {
        cout << "enter word to look for, or q to quit:" << endl;
        string s;
        // stop if we hit end-of-file on the input or if a 'q' is entered
        if(!(cin >> s) || s == "q")
            break;
        // run the query and print the results
        print(cout, tq.query(s)) << endl;
    }
}


int main(int argc, const char * argv[])
{
    // open the file from which user will query words
    ifstream infile;
    // open returns void, so we use the comma operator XREF(commaOp)
    // to check the state of infile after the open
    if (argc < 2 || !(infile.open(argv[1]), infile)) {
        cerr << "No input file!" << endl;
        return EXIT_FAILURE;
    }
    runQueries(infile);

    cout << "hello world" << endl;
    return 0;
}

运行结果:

 

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

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

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