针对特定XML的解析器XMLParser

一、建立网页库和偏移文件

为文本搜索引擎建立网页库,首先要把所有的网页(这里是文章)格式化,并保存到指定的格式中。如以下格式:
   |                                  
   |    <doc>                        
   |       <docid>...</docid>      
   |       <url>...</url>           
   |       <title>...</title>         
   |       <content>...</content>     
   |    </doc>                      
   |    <doc>                       
   |      ...                         
   |    </doc>                        
   |    ...                         

其中<doc>……</doc>保存着一篇文章;<docid>...</docid> 保存文档id;<url>...</url>保存文本路径;<content>...</content> 保存文章内容。

如何生成这样的格式呢?对每一篇文章使用以下方法:

首先从文章中提取标题 title;以及获得文章的路径,然后用字符串拼接,拼接成上述格式。

std::ofstream ofs("pages.lib",std::ios::out);//打开网页库
std::ofstream out("offset.lib",std::ios::out); //打开偏移文件

string
txt = "<doc><docid>" + docid + "</docid>" + "<url>" + url + ... + "</doc>"; ofs << txt;     //写到文件流 int offset = ofs.tellg();// offset 获得当前指针的位置 ,tellg()和tellp()是C++文件流操作中获得流指针的函数 int length = txt.size();//length out<<docid<<" "<<offset<<""<<length<<endl;//将这片文章的起始位置和偏移量写到偏移文件中,便于后面从网页库中读出一篇网页

循环对所有文章执行上面的操作,就得到一个网页库和一个偏移文件。

二、解析网页

根据偏移文件,从网页库中读出一篇网页,接着对网页库中的docid,title,url进行解析。

以下是解析器的代码:

 1 #ifndef _XMLPARSER_HPP
 2 #define _XMLPARSER_HPP
 3 #include <stdio.h>
 4 #include <utility>
 5 #include <string>
 6 #include <stdlib.h>
 7 using namespace std;
 8 class XMLParser
 9 {
10 public:
11     XMLParser(string & page)
12             :page_(page)
13     {}
14     string parser(string tag)
15     {
16         string ltag,rtag;
17         ltag = "<"+tag+">";        //拼接左标签
18         rtag = "</"+tag+">";    //拼接右标签
19         string str;
20         //pair<string,string> pr;
21         string::size_type bpos;
22         string::size_type epos;
23         string::size_type len;
24         //确定字符串范围
25         bpos = page_.find(ltag);//查找作标签下标
26         epos = page_.find(rtag);//查找右标签下标
27         bpos+=ltag.size();
28         len = epos - bpos;        //计算标签包含内容长度
29         if(bpos==page_.npos||epos==page_.npos)
30         {
31             printf("No such tag: %s \n",tag.c_str());
32             exit(-1);
33         }
34         if(bpos==epos)
35         {
36             printf("标签内容不存在\n");
37             exit(-1);
38         }
39 
40         string content = page_.substr(bpos+1,len-2);//截取标签包含内容
41 #if 0
42         if(content[content.size()-1]=='\n')
43             content[content.size()-1]=='\0';
44         if(content[content.size()-2]=='\n')
45             content[content.size()-2]=='\0';
46         if(content[content.size()-3]=='\n')
47             content[content.size()-3]=='\0';
48 #endif
49         //pr.first = tag;
50         //pr.second = content;
51         //return pr;    //最好使用std::move,减少复制
52         return content;
53     }
54 private:
55     string &page_;    //使用引用,避免大复制
56 };
57 #endif


解析器每次只能传入一个标签,并且返回标签对应的内容,要对所有标签进行解析,那么就要多次调用parser()。当然,对于一篇文章,就对应一个解析器对象,parser()是对外的一个函数,接收标签,并从对象中解析出便签包含的内容。也就是说,创建一个对象可以对这个对象调用多次parser(string tag)对文章进行解析。

三、解析器的使用

首先要有一个加载偏移文件的工具,这里写了一个工具PageOffset.hpp

(代码折叠)代码如下:

#ifndef _PAGEOFFSET_HPP
#define _PAGEOFFSET_HPP

#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <utility>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

class PageOffset
{
public:
    PageOffset(string &path)
    {
        ifstream ifs(path.c_str(),ios::in);
        string line;
        string str;
        pair<int,int> p1;
        pair<int ,pair<int ,int > > p2;
        while(getline(ifs,line),!ifs.eof())
        {
            istringstream iss(line);
            int pos = 0;
            while(iss>>str)
            {
                if(pos == 0)
                {
                    p2.first = atoi(str.c_str());    
                }
                else if(pos == 1)
                {
                    p1.first = atoi(str.c_str());
                }
                else if(pos == 2)
                {
                    p1.second = atoi(str.c_str());
                    p2.second = p1;
                }
                pos++;
            }
            offset.insert(p2);
        }
        ifs.close();
    }

    pair<int ,int > &operator[](int docid) 
    {
        return offset[docid];
    }

    size_t size() const
    {
        return offset.size();
    }
private:
    map<int,pair<int,int> > offset;
};
#endif
View Code

测试程序代码如下:

 1 #include "XMLParser.hpp"
 2 #include "PageOffset.hpp"
 3 #include <fstream>
 4 #include <cstring>
 5 #include <string>
 6 #include <utility>
 7 using namespace std;
 8 int main()
 9 {
10     ifstream ifs("pages.lib",ios::in);
11     PageOffset pageoffset("offset.lib");
12     char *buf = new char[1024*1024];                //开缓存空间
13     pair<int,int> page =(pageOffset)[vec[i]];        //从偏移文件中提取文档相应的offset与size,读出一篇文章
14     ifs.seekg(page.first,ios::beg);                    //定位文件起始位置
15     memset(buf,0,1024*1024);
16     ifs.read(buf,page.second);                        //读取一篇文档
17     string str(buf);                                //C风格的字符串转化为C++风格的字符串
18     XMLParser xmlparser(str);                        //将文章加入解析器,初始化一个解析器对象
19     string title = xmlparser.parser("title");        //解析标题
20     string url = xmlparser.parser("url");            //解析url
21     string content = xmlparser.parser("content");    //解析内容
22     delete []buf;                                    //释放缓存空间,防止内存泄露
23     return 0;
24 }

以上是针对特定格式的文本库进行解析。因此不能给适用于多种场合。

 

posted @ 2015-10-10 15:01  HOU_JUN  阅读(1001)  评论(0编辑  收藏  举报