给lemur加中文分词续

上次写的博客中提到了给lemur加入中文分词的一种办法,但是现在经过测试发现那种办法有个缺点,就是说对于想使用field 的用户来说会出现问题,如果采用我上次说的办法,在用field建立好的索引就会出现丢失一些field中的词的情况,比如隊于这样的文档:
  <DOC>
        <DOCNO>1<DOCNO>
        <TEXT>
             <AUTHOR>小黑</AUTHOR>
             <CONTENT>我热爱我的祖国,我热爱我的家!</CONTENT>
        </TEXT>
  </DOC>
如果采用如下的parameters建立索引
 <parameters>
        <corpus>
                <path>/home/ubuntu/Desktop/lemur/indexcode/text/</path>
                <class>trectext</class>
        </corpus>
        <field>
                <name>DOCNO</name>
        </field>
        <field>
                <name>TEXT</name>
        </field>
        <field>
                <name>AUTHOR</name>
        </field>
        <field>
                <name>CONTENT</name>
        </field>
        <memory>256m</memory>
        <index>/home/ubuntu/Desktop/lemur/indexcode/index</index>
</parameters>
在检索的时候你可以通过如下的命令
IndriRunQuery -index=./index/ -query="#combine[content](热爱.content)" -printPassages=true
发现在content这个field中只有"我"和“热爱”两个词,如果你在content这个field中检索其他的词是不会有任何结果的,经过观察和猜测发现原因出现在indri::api::ParsedDocument中的tags这个向量上,在它的里面记录了一个field中的词是从第几个词开始从第几个词结束,而由于我的办法是在tokenize之后进行分词,然后用分好词的结果去替代terms,用新统计好的位置信息去替代positions,但是却忽略了tags这个向量,如果打印看的话,隊于上面的文档,可以看到tags标签隊于名字是<content>的field的begin是1,end是3。所以我们在分词的时候还要同时统计一下这个相关信息,然后利用新的开始和结束信息来替换indri::api::ParsedDocument中的tags的开始和结束信息,这样就万事大吉了!

给出我的整个代码如下:
void IndexEnvironment::addFile( const std::string& fileName, const std::string& fileClass ) {
   
  if (!LoadSegRes())  //加载分词的资源(分词程序是实验室的程序)
  {
    cerr << "Can not loadSegRes!" << endl;
    exit(-1);
  }
  void* pSegger = CreateSegger(); //创建分词器
  SetOption(pSegger, 1, 1, 0); //设置相关参数
 
  //编码转换的类,从utf-8到gb2312,实验室的分词只能切分gb2312的文字,所以作这个转换
  CodeConvert ccu2g = CodeConvert("utf-8","gb2312");
  CodeConvert ccg2u = CodeConvert("gb2312","utf-8");

  indri::parse::Parser* parser = 0;
  indri::parse::Tokenizer* tokenizer = 0;
  indri::parse::DocumentIterator* iterator = 0;
  indri::parse::Conflater* conflater = 0;
 
  _getParsingContext( &parser, &tokenizer, &iterator, &conflater, fileClass );

  if( !parser || !iterator ) {
    _documentsSeen++;
    if( _callback ) (*_callback) ( IndexStatus::FileSkip, fileName, _error, _documentsIndexed, _documentsSeen );
  } else {
    try {
      indri::parse::UnparsedDocument* document;
      indri::parse::TokenizedDocument* tokenized;
      indri::api::ParsedDocument* parsed;

      iterator->open( fileName );

      std::vector<indri::parse::Transformation*> annotators = _createAnnotators( fileName, fileClass, &conflater );

      // notify caller that the file was successfully parsed
      if( _callback ) (*_callback)( IndexStatus::FileOpen, fileName, _error, _documentsIndexed, _documentsSeen );

      while( document = iterator->nextDocument() ) {
        _documentsSeen++;

        tokenized = tokenizer->tokenize( document );
        indri::utility::greedy_vector<char*> terms; //这个存放分词结果
        indri::utility::greedy_vector<indri::parse::TermExtent> positions; //存放新的位置信息
        int len = tokenized->terms.size();

        int total_size = 0;
        //计算分词之后结果所需要的空间,因为terms存放的是char*,需要用Buffer类开辟空间
        for(int i = 0; i < len; i++)
        {
            char* s = tokenized->terms[i];
            int size = strlen(s)*2;
            total_size += (++size);
        }
        indri::utility::Buffer _termBuffer(total_size); //开辟terms中char* 的空间
   
        int tag_size = tokenized->tags.size(); //统计tag信息
        int p_tag_end[tag_size/2]; //tokenized中的tag存放的是从类似position位置信息,
        int c_tag_end[tag_size/2]; //parsed中tag存放的是第几个词的位置信息
   
        for(int i = 1; i < tag_size; i+=2)
        {
            p_tag_end[i/2] = tokenized->tags[i].end;
            c_tag_end[i/2] = 0;
        }

        for(int i = 0; i < len ; i++)
        {
            int begin = tokenized->positions[i].begin;
            int k = 0;
            for(k = 0; k < tag_size; k++) //找到待切分的句子属于哪个field
            {
                if(begin < p_tag_end[k]){break;}
            }
             //得到待切分句子,tokenized已经按照空格和标点切好了,这里可以直接得到
            char* s = tokenized->terms[i];
            int size = strlen(s);
            if(size == 0) {continue;}
            char out[size*2];
            ccu2g.convert(s,size,out,size*2); //编码转换
            vector<string> vecWords;
            string temp(out);
            WordSegment(pSegger, temp, vecWords); //分词,结果存放在vecWords中
            for(int j = 0; j < vecWords.size(); j++)
            {
                c_tag_end[k]++; //统计新分词的tag信息
                char* word= (char*)vecWords[j].c_str();
                int wordsize = vecWords[j].size();//strlen(word);
                char outword[wordsize*2];
                ccg2u.convert(word, wordsize, outword, wordsize*2); //转换回utf编码
                string w(outword);
                char* write_loc = _termBuffer.write( w.size()+1 ); //写term到termbuffer中
                strncpy( write_loc, outword, w.size() );       
                write_loc[w.size()] = '/0';
           
                terms.push_back(write_loc); //把词加入terms向量中去
                //统计新的位置信息
                int usize = strlen(outword);
                indri::parse::TermExtent term_extent;
                term_extent.begin = begin;
                term_extent.end = usize + begin;
                begin += usize;
                positions.push_back(term_extent);
            }
            //改变后续的tag的end信息,使得信息准确
            for(int j = k+1; j < tag_size/2; j++ ){c_tag_end[j] = c_tag_end[k];}
        }
     
        parsed = parser->parse( tokenized );       
        parsed = _applyAnnotators( annotators, parsed );

        //替换相关信息,terms和positions
        parsed->terms = terms;
        parsed->positions = positions;

        //得到新的tags信息,前一个tag的end是后一个tag的begin
        if(tag_size > 0) parsed->tags[0]->end = c_tag_end[0];
        for(int i = 1; i < tag_size/2; i++)
        {
            parsed->tags[i]->begin = c_tag_end[i-1];
            parsed->tags[i]->end = c_tag_end[i];
        }

        _repository.addDocument( parsed );

        _documentsIndexed++;
        if( _callback ) (*_callback)( IndexStatus::DocumentCount, fileName, _error, _documentsIndexed, _documentsSeen );
      }

      // notify caller that the file was successfully closed
      if( _callback ) (*_callback)( IndexStatus::FileClose, fileName, _error, _documentsIndexed, _documentsSeen );

      iterator->close();

    } catch( lemur::api::Exception& e ) {
      if( iterator )
        iterator->close();

      // notify caller of errors
      if( _callback ) (*_callback)( IndexStatus::FileError, fileName, e.what(), _documentsIndexed, _documentsSeen );
    }
  }
   
  ReleaseSegger(pSegger); //释放分词资源
  ReleaseSegRes();
}



上述就是我的代码,由于是实验品,加之我的c++水平问题,所以写的有效率不足之处一定很多,但是我实验过功能没有任何问题,目前看来稳定性也不错,下一步将把分词移和编码转换移到全局空间去,以提高效率。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值