首页 > 代码库 > 【足迹C++primer】56、文本查询程序

【足迹C++primer】56、文本查询程序

/**
* 功能:文本查询程序
* 时间:2014年7月23日10:26:09
* 作者:cutter_point
*/

#include<iostream>
#include<algorithm>
#include<memory>
#include<set>
#include<map>
#include<fstream>
#include<sstream>

using namespace std;

/*
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?"
*/

/*
Executing Query for:
Daddy Daddy occurs 3 times
(line 2) Her Daddy says when the wind blows
(line 7) "Daddy, shush, there is no such thing,"
(line 10) Shyly, she asks, "I mean, Daddy, is there?"

Executing Query for: ~(Alice)
~(Alice) occurs 9 times
(line 2) Her Daddy says when the wind blows
(line 3) through her hair, it looks almost alive,
(line 4) like a fiery bird in flight.
...

Executing Query for: (hair | Alice)
(hair | Alice) occurs 2 times
(line 1) Alice Emma has long flowing red hair.
(line 3) through her hair, it looks almost alive,

Executing query for: (hair & Alice)
(hair & Alice) occurs 1 time
(line 1) Alice Emma has long flowing red hair.
*/

/**************************************
面向对象的解决方式
**************************************/

/*
?&操作符将生成一个查询绑定到一个新的AndQuery。
?的|操作符将生成一个查询绑定到一个新的orquery。
?的~操作符将生成一个查询绑定到一个新的NotQuery对象。
?查询构造函数的字符串将生成一个新的WordQuery。
*/

//以前的基础类
class QueryResult;
class TextQuery //用来存储输入的文件!!
{
public:
    using line_no=vector<string>::size_type;    //unsigned int类型
    TextQuery(ifstream&);   //构造函数输入文件流
    QueryResult query(const string&) const;
private:
    shared_ptr<vector<string>> file;        //输入文件,vector里面每个元素代表文件的一行字符串
    //每个单词到它所在的行数的集合映射
    map<string, shared_ptr<set<line_no>>> wm;
};

//读取文件的每一行并建立单词到行号的索引
//这个是整个程序真正对file进行操作的核心代码!!!!
TextQuery::TextQuery(ifstream& is):file(new vector<string>)
{
    string text;    //赋值为一行文本程序
    while(getline(is, text))    //吧文件的一行文本放到text里面
    {
        file->push_back(text);  //吧一行文本放到vector里面去
        int n=file->size();     //file是一个vector吧string放进去之后得到当前是第几个
        //吧一行分解为一堆单词
        istringstream line(text);   //吧text和istringstream类型绑定
        string word;    //得到每一个单词
        while(line>>word)   //重每行中输出到word,每次以空格结束
        {
            //单词如果不再wm中,那么就加进去,这里key值就是word,如果以前没有word这个插入之后
            //返回值就是变成了1,这里value是一个指针,也就是所set里面是这个单词出现的行数
            shared_ptr<set<line_no>> &lines=wm[word];   //lines是一个shared_ptr,有了key值,但是没有value
            if(!lines)  //如果lines为假,就是lines指向为空时
            {
                //就是说,当lines指向为空
                lines.reset(new set<line_no>);  //吧a指向的内置指针b,否则置空,a.reset(b)
            }

            lines->insert(n);   //将当前的行数插入到lines中
        }
    }
}


class QueryResult   //存放查找到的结果
{
    friend ostream& print(ostream&, const QueryResult&);
public:
    using line_no=vector<string>::size_type;
//    typedef std::set<line_no>::const_iterator line_it;

    //构造函数参数列表分别是,要找的单词,出现行数的集合,所有行数字符串的集合
    QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> f):sought(s),lines(p),file(f){}
    shared_ptr<vector<string>> get_file() {return file;}    //得到文件后面NotQuery使用
    set<line_no>::iterator begin(){return lines->begin();}  //得到第一次出现的行数
    set<line_no>::iterator end() {return lines->end();}     //得到最后一次出现的行数

private:
    string sought;      //要查询的单词
    shared_ptr<set<line_no>> lines;     //出现的行号,这里lines是一个shared_ptr的指针
    shared_ptr<vector<string>> file;       //相应的文本源
};

//进行比较,是返回word还是ending
inline string make_plural(size_t ctr, const string& word, const string& ending)
{
    return (ctr>1) ? word+ending : word;
}

//打印结果的函数
ostream& print(ostream& os, const QueryResult& qr)
{
    //如果找到了单词,打印出现的次数,和出现的位置
    //如果找到了单词,打印出现的次数
    os<<qr.sought<<" 出现 "<<qr.lines->size()<<"  "<<make_plural(qr.lines->size(), "time", "s")<<endl;
    //打印单词出现的每一行字符串
    for(auto num : *qr.lines)   //qr.lines指向的是shared_ptr<set<size_t>>,set里面存放出现单词的每一行
    {
        //qr指向的对象是QueryResult,qr.file->begin()+num-1指向当前行的字符串
        os<<"\t(第 "<<num<<" 行)"<<*(qr.file->begin()+num-1)<<endl;
    }

    return os;

}

//map<string, shared_ptr<set<line_no>>> wm;
//定义QueryResult在TextQuery和QueryResult之后
//在wm中寻找sought每个单词到它所在的行数的集合映射
QueryResult TextQuery::query(const string& sought) const
{
    //如果没有找到sought这个单词的话,那么返回一个指向set的指针
    static shared_ptr<set<line_no>> nodata(new set<line_no>);
    //使用泛型算法find来查找,运用下标的话会导致添加单词
    auto loc=wm.find(sought);

    //构造函数参数列表分别是,要找的单词,出现行数的集合,所有行数字符串的集合
    //QueryResult(string s, shared_ptr<set<line_no>> p,
    //shared_ptr<vector<string>> f):sought(s),lines(p),file(f){}
    if(loc == wm.end())
        return QueryResult(sought, nodata, file);   //没有找到的时候
    else
        return QueryResult(sought, loc->second, file);  //loc->second第二个参数也可以loc[sought]??但是我一用就没法通过编译了!!!!
}

//最基础的抽象类
class Query_base
{
    friend class Query;
protected:
    using line_no=TextQuery::line_no;
    virtual ~Query_base()=default;
private:
    virtual QueryResult eval(const TextQuery&) const=0; //TextQuery构造函数对象是一个文件
    virtual string rep() const=0;

};

class Query
{
    friend Query operator~(const Query&);
    friend Query operator|(const Query&, const Query&);
    friend Query operator&(const Query&, const Query&);
public:
    Query(const string&);
    QueryResult eval(const TextQuery &t) const {return q->eval(t);} //t的类型是一个文本文件
    string rep() const {return q->rep();}   //没什么意义
private:
    Query(shared_ptr<Query_base> query):q(query) {} //私有类外面不能构造Query对象
    shared_ptr<Query_base> q;

};

//重载<<运算符,输出rep,当<<后面接一个Query类对象的时候直接输出它的rep()
ostream & operator<<(ostream &os, const Query &query)
{
    return os<<query.rep();
}

class WordQuery : public Query_base     //单个单词查询对象
{
    friend class Query; //友元!!
    WordQuery(const string &s) : query_word(s) {}   //s就是要查询的单词
    //调用query(const string& s)就是给TextQuery吧找到的单词,行数,每行字符串给赋值好
    QueryResult eval(const TextQuery &t) const {return t.query(query_word);}
    string rep() const {return query_word;} //返回要查询的单词
    string query_word;      //要找的单词
};

//这里吧Query里面的构造函数实现了,单纯就是调用wordQuery的构造函数
inline Query::Query(const string &s):q(new WordQuery(s)){}

//非运算查询
class NotQuery : public Query_base
{
    friend Query operator~(const Query &);  //
    NotQuery(const Query &q):query(q){}
    string rep() const {return "~("+query.rep()+")";}
    QueryResult eval(const TextQuery &) const;
    Query query;
};

inline Query operator~(const Query & operand)
{
    return shared_ptr<Query_base>(new NotQuery(operand));
}

class BinaryQuery : public Query_base
{//这个类没有override eval但是它继承了抽象类的eval所以这个类也是抽象类
protected:
    BinaryQuery(const Query &l, const Query &r, string s):lhs(l), rhs(r), opSym(s){}
    string rep() const {return "("+lhs.rep()+" "+opSym+" "+rhs.rep()+")";}
    Query lhs, rhs; //代表要进行相应操作的两个单词
    string opSym;   //操作符标示

};

class AndQuery : public BinaryQuery
{
    friend Query operator & (const Query&, const Query&);
    AndQuery(const Query& left, const Query& right):BinaryQuery(left, right, "&"){}
    QueryResult eval(const TextQuery&) const;
};

inline Query operator & (const Query& lhs, const Query& rhs)
{
    return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}

class OrQuery : public BinaryQuery
{
    friend Query operator | (const Query&, const Query&);
    OrQuery(const Query& left, const Query& right):BinaryQuery(left, right, "|"){}
    QueryResult eval(const TextQuery&) const;
};

inline Query operator | (const Query& lhs, const Query& rhs)
{
    return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}

///函数的定义eval
/**
NotQuery的eval实现
*/
//QueryResult eval(const TextQuery &) const;
QueryResult NotQuery::eval(const TextQuery& text) const
{
    //QueryResult eval(const TextQuery &t) const {return q->eval(t);}
    //t的类型是一个文本文件
    auto result=query.eval(text);   //返回查询结果
    auto ret_lines=make_shared<set<line_no>>(); //这是出现的行数
    auto beg=result.begin(), end=result.end();
    auto sz=result.get_file()->size();

    for(size_t n=0 ; n != sz ; ++n)
    {
        if(beg == end || *beg != n) //如果到了末尾或者beg指向的对象的数字不是n
            ret_lines->insert(n);   //只要不是找到的行数,那么就加入,意思就是找的就是含有这个单词的,而这个类是要找不含有的
        else if(beg != end)
            ++beg;
    }

    return QueryResult(rep(), ret_lines, result.get_file());
}

/**
AndQuery的eval实现
*/
QueryResult AndQuery::eval(const TextQuery& text) const
{
    //BinaryQuery中Query lhs, rhs; 代表要进行相应操作的两个单词
    //QueryResult eval(const TextQuery &t) const {return q->eval(t);}
    //t的类型是一个文本文件
    auto left = lhs.eval(text), right = rhs.eval(text); //调用的是WordQuery的eval
    auto ret_lines = make_shared<set<line_no>>();

    //set_intersection:同时包含第一个和第二个集合中的元素,
    //这些元素被复制到最后一个参数中,
    //最后一个参数是一个保存这些元素的容器的首迭代器,
    //这个容器必须预先分配足够的空间来容纳元素。
    //两个集合都必须是有序的且是相同的排序。找出交集
    set_intersection(left.begin(), left.end(), right.begin(), right.end(),
            inserter(*ret_lines, ret_lines->begin()));
    return QueryResult(rep(), ret_lines, left.get_file());
}


/**
OrQuery的eval实现
*/
QueryResult OrQuery::eval(const TextQuery& text) const
{
    auto right = rhs.eval(text), left = lhs.eval(text);
    auto ret_lines = make_shared<set<line_no> >(left.begin(), left.end());
    //吧right从头到尾拷贝到ret_lines后面
    ret_lines->insert(right.begin(), right.end());
    return QueryResult(rep(), ret_lines, left.get_file());
}


void runQueries(ifstream &infile)
{
    //infile是一个ifstream,指向我们要处理的文件
    TextQuery tq(infile);   //保存文件并建立查询map
    //与用户交互:提示用户输入要查询的单词,完成查询并打印结果
    while(true)
    {
        cout<<"enter word to look for , or q to quit: ";
        string s;
        //若遇到文件结尾或输入'q'时结束循环
        if(!(cin>>s) || s == "q")
            break;
        //指向查询并打印结果
        print(cout, tq.query(s))<<endl;
    }
}



int main()
{
    ifstream infile;    //文本文件
    infile.open("test.txt");

    TextQuery file=infile;  //文件有了

     //Query q = ~Query("Alice");
    //Query q = Query("hair") | Query("Alice");
    //Query q = Query("hair") & Query("Alice");
    Query q = Query("fiery") & Query("bird") | Query("wind");

    const auto result=q.eval(file);

    cout<<"\n好的这个结果是:"<<q<<endl;

    print(cout, result)<<endl;

    infile.close();

    system("pause");
    return 0;
}


这个primer基本就快结束了,第五部分我不打算看了,因为接下来我会看《thinking in C++》有些地方那里面会讲到,这里就了解一下就可以了,灵活运用在thinking in c++l里面进一步深入,而且英语不好这英文版看起来确实很吃力,并且好多东西根本看不懂,很多人说英文版比中文版好很多,我想说的是,对于过4级很勉强的孩子,还是安心看中文版吧,不然看英文版好多细节都会漏掉,至少我4级飘过看英文版是可以看,但是效率你可以预料,我一天都看不到10面的,实打实的一天,不说多的最少8小时是有的,但是要看玩,敲出代码,并且理解,网上查资料,就看这么点,以前中文版吧,2个小时的事,现在搞一天,而且感觉效果并没有说英文版比中文版好的多,翻译并不是完全没法看,只是个别地方翻译有问题,网上查一下就出来了,没必要非要看英文版,哎 我是中文版的书掉了,网上找不到中文版,就只能看看英文版了。。。。。