C++ primer 文本查询程序 Query

本文介绍了一个使用 C++ 实现的面向对象文本查询程序。该程序能够读取文本文件并建立索引,支持基本的布尔查询操作如 AND、OR 和 NOT。通过对类 TextQuery 的设计,展示了如何有效地组织代码来完成文本检索任务。

C++ primer 文本查询程序,面向对象的实现,有很多值得学习的知识点。

整理了代码如下,使用VS2010,g++编译通过:


#include <map>
#include <set>
#include <vector>
#include <string>

#include <algorithm>
#include <iterator>
#include <istream>
#include <sstream>  //istringstream
using std::istringstream;

using std::map;
using std::set;
using std::vector;
using std::string;

#include <iostream>
using std::ostream;
using std::cout;
using std::cin;
using std::cerr;
using std::endl;

#include <fstream>
using std::getline;
using std::ifstream;


/**
 * @brief The TextQuery class
 */
class TextQuery
{
public:
    typedef vector<string>::size_type line_no;
    void read_file(std::ifstream &is)
    {
        store_file(is);
        build_map();
    }

    set<line_no> run_query(string &) const;
    string text_line(line_no) const;
    line_no size() const   //the total lines num
    {
        return lines_of_text.size();
    }

private:
    void store_file(std::ifstream &);
    void build_map();
    vector<string> lines_of_text;
    map<string,set<line_no> > word_map;
};


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

    return set<line_no>();
}
string TextQuery::text_line(line_no line) const
{
    return lines_of_text[line];
}

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

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




/**
 * @brief The Query_base class
 */
class Query_base
{
    friend class Query;
protected:
    virtual ~Query_base()
    {

    }

private:
    virtual set<TextQuery::line_no> eval(const TextQuery &) const = 0;
    virtual ostream & display(ostream &os = std::cout ) 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 &word); //builds a new wordQuery

    //copy control to manage pointers and counting
    Query(const Query &c):q(c.q),use(c.use)
    {
        ++*use;
    }

    ~Query()
    {
        decr_use();
    }

    Query &operator=(const Query &query)
    {
        q = query.q;
        ++*use;
        return *this;
    }

    set<TextQuery::line_no> eval(const TextQuery &t) const
    {
        return q->eval(t);
    }

    std::ostream &display(std::ostream &os) const
    {
        return q->display(os);
    }

private:
    Query(Query_base *query):q(query),use(new std::size_t(1))
    {

    }
    Query_base *q;
    std::size_t *use;
    void decr_use()
    {
        if ( --*use == 0 )
        {
            delete q;
            delete use;
        }
    }
};

inline ostream & operator<<(ostream &os,const Query &query)   //why needed const for Query &??
{
    query.display(os);
    return os;
}

/**
 * @brief The WordQuery class
 */
class WordQuery : public Query_base
{
    friend class Query;
    WordQuery(const string &s):query_word(s)
    {

    }

    set<TextQuery::line_no> eval(const TextQuery &t) const
    {
        return t.run_query((string &)query_word);   //why needed const?
    }
    ostream & display(ostream &os = std::cout ) const;
    string query_word;
};

ostream & WordQuery::display(ostream &os) const
{
    return os<<query_word;
}

//Query constructor!! we can't define before the WordQuery class finish
Query::Query(const string &word) //builds a new wordQuery
{
    q = new WordQuery(word);
    use = new std::size_t(1);
}

/**
 * @brief The NotQuery class
 */
class NotQuery :public Query_base
{
    friend Query operator~(const Query &);
    NotQuery(const Query &q):query(q)
    {

    }

    set<TextQuery::line_no> eval(const TextQuery &) const;   //becareful the last const,if you forget to add it in the implement,it overloads and bug
    ostream & display(ostream &os) const;
    const Query query;   //const is ok,when you initianize in the constructor
};

set<TextQuery::line_no> NotQuery::eval(const TextQuery &file) const
{
    set<TextQuery::line_no> has_val = query.eval(file);
    set<TextQuery::line_no> ret_lines;

    TextQuery::line_no n = 0;
    for ( ; n != file.size() ; ++n )
    {
        if ( has_val.find(n) == has_val.end() )
        {
            ret_lines.insert(n);
        }
    }

    return ret_lines;
}

ostream & NotQuery::display(ostream &os) const
{
    return os<<"~("<<query<<")";
}

/**
 * @brief The BinaryQuery class
 */
class BinaryQuery : public Query_base
{
protected:   //can't not be private
    BinaryQuery(Query left,Query right,string op):lhs(left),rhs(right),oper(op)
    {

    }

    ostream & display(ostream &os = std::cout ) const
    {
        return os<<"("<<lhs<<" "<<oper<<" "<<rhs<<")";
    }

    Query lhs,rhs;
    string oper;   //& |
};

/**
 * @brief The AndQuery class
 */
class AndQuery : public BinaryQuery
{
    friend Query operator&(const Query &,const Query &);
    AndQuery(const Query &left,const Query &right):BinaryQuery(left,right,"&")
    {

    }

    set<TextQuery::line_no> eval(const TextQuery &) const;
};


set<TextQuery::line_no> AndQuery::eval(const TextQuery &file) const
{
    set<TextQuery::line_no> left = lhs.eval(file),
    right = rhs.eval(file);

    set<TextQuery::line_no> ret_lines;
    set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(ret_lines,ret_lines.begin()));

    return ret_lines;
}

/**
 * @brief The OrQuery class
 */
class OrQuery : public BinaryQuery
{
    friend Query operator|(const Query &,const Query &);
    OrQuery(const Query lq,const Query rq):BinaryQuery(lq,rq,"|")
    {

    }

    set<TextQuery::line_no> eval(const TextQuery &) const;
};


set<TextQuery::line_no> OrQuery::eval(const TextQuery &file) const
{
    set<TextQuery::line_no> right = lhs.eval(file),
    ret_lines = rhs.eval(file);

    ret_lines.insert(right.begin(),right.end());
    return ret_lines;
}

Query operator~(const Query &query)
{
    return new NotQuery(query);
}

Query operator|(const Query &lhr,const Query &rhr)
{
    return new OrQuery(lhr,rhr);
}

Query operator&(const Query &lhr,const Query &rhr)
{
    return new AndQuery(lhr,rhr);
}


void print_result(const set<TextQuery::line_no> &locs,const TextQuery &file)
{
    typedef set<TextQuery::line_no> line_nums;
    line_nums::const_iterator iter = locs.begin();
    for( ; iter != locs.end() ; iter++ )
    {
        cout<<"\tline"<<(*iter)+1<<")"<<file.text_line(*iter)<<endl;
    }
}

bool open_file(ifstream &is,char *filename)
{
    if ( is.is_open() )
    {
        is.close();
    }

    is.open(filename);

    if ( is.is_open() == true )
    {
        return true;
    }

    return false;
}


int main()
{

    ifstream infile;
    if ( !open_file(infile,(char *)"TextQuery.txt"))
    {
        cerr<<"No input file!"<<endl;
        return -1;
    }

    TextQuery tq;
    tq.read_file(infile);

    //Query q = Query("I") & Query("like") | Query("you");
    //Query q = Query("you") & Query("like");
    Query q = Query("I") & Query("like");
    set<TextQuery::line_no> res = q.eval(tq);
    q.display(cout);

    cout<<endl;
    print_result(res,tq);
}

测试文件为 TextQuery.txt ,内容如下,

I like you and hope you like me!
But you don't like me,I know that,I am so sad!
One day,will you like me?




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值