CLucene加入ICTCLAS中文分词

2023-11-10 16:58

本文主要是介绍CLucene加入ICTCLAS中文分词,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 最近,我在开发桌面搜索软件,其中桌面搜索最核心的部分就是全文检索。之前已经完成了一个初始版本。全文检索是使用的中科院计算所郭博士的Firtex,后来有位老师说Firtex最近没人在维护,建议使用CLucene,于是我老板就提议再开发另一个版本——CLucene版的桌面搜索。

CLucene是C++版的Lucene,提供全文检索的功能。在网上找了部分资料,主要都是如何在CLucene中加入中文分词。于是我根据网上的资料和自己在开发过程中对CLucene的理解,开始向CLucene加入中文分词。听说ICTCLAS的中文分词很出名,就拿来试试。

CLucene使用的是0.9.21版,同时通过svn更新到最新的文件,下载地址是

http://sourceforge.net/projects/clucene/。

ICTCLAS中文分词使用是ICTCLAS3.0,这是共享版,但不开源,可以增加用户自定义词典,下载地址

http://www.ictclas.org/Download.html。

CLucene只支持Unicode的编码,所以对需要对输入的字符串都要进行转换。在Windows下可以使用api WideCharToMultiByte和MultiByteToWideChar。在Linux下,可以使用linux下有著名的iconv。

我的开发平台是windows,vs2005。在CLucene中加入中文分词,分三步完成。

1、把项目设置为Use Unicode Character Set。因为使用ANSI时,汉字存在与其他语言编码重叠的问题,不能准确判断是否为汉字。一般下载的CLucene默认就是设置好的。

2、 \src\CLucene\util\Misc.cpp中有个Misc::_cpycharToWide函数,这个函数是CLucene中用来将char字符串转换为wchar_t字符串的,但原代码中使用的是不考虑编码的转换,把汉字从ANSI编码转到UCS2编码会失效,所以需要使用MultiByteToWideChar进行转换。同样的在同一个文件中,有这样一个函数Misc::_cpywideToChar,是将wchar_t转换成char,同理使用WideCharToMultiByte进行转换。

3、也是最麻烦的一步。如果了解Lucene结构的开发者,应该知道Lucene进行索引前需要对文本进行分词,就是需要使用Analyzer类进行分词。在CLucene中有一个StandardAnalyzer类,它本意是可以处理亚洲语言(包括中日韩),但不完善。可以通过改写 \src\CLucene\analysis\standard\StandardTokenizer.cpp文件来达到目的。因此,在这一步有二种方法,一是上面所说的,修改StandardTokenizer,让它支持中文分词。另一个就是自己写一个ChineseTokenizer和ChineseAnalyzer,在索引的时候调用它就好了,同样查询的时候也要ChineseAnalyzer进行分析。

第一种方法:在StandardTokenizer中有一个StandardTokenizer::next()方法,用来获取下一个token。Token可以简单看成是索引文件中的一个项。在原代码中,先判断是否为ALPHA,再判断是否为_CJK,而在中文windows系统上,将一个UCS2编码的汉字(正好一个wchar_t)作为参数传给 iswalpha函数时,返回值是true。所以任何CJK字符都会被处理成ALPHA。因此,项目修改为使用Unicode编码后,ReadCJK函数 不会被调用到。所以需要将原代码中的if(_CJK)的判断分支放到if(ALPHA)前面,这样遇到CJK字符时就会调用 StandardTokenizer::ReadCJK。ReadCJK函数用于读取一个CJK的token。这个函数不能处理中文分词,它遇到CJK字符时一直向后读取,直到遇到非CJK字符或文件结束才跳出,把读取到的整个字符串作为索引中的一个项。所以需要重写这个函数。我个人认为这个方法需要修改原代码地方太多,而且代码重用率低,所以我选择了第二种方法。

第二种方法:就是自己重新写一个ChineseAnalyzer和ChineseTokenizer。这样对原代码修改少,改错也方便,最重要可以将它应用其他需要的程序中。其实这两个类也并不难写,只要根据StandardTokenizer和StandardAnalyzer的结构,模仿一下就行了。ChineseTokenizer是继承Tokenizer类的,必须实现Tokenizer类的next()虚成员函数。这个成员函数就是要输入下一个分词的字符串。所以在这里将ICTCLAS加入,每次将一个分词输出就好。因此ICTCLAS只支持char类型分词,所以还要将Unicode转换成char,分词使用ParagraphProcess函数。关键代码如下:


str=STRDUP_TtoA(ioBuffer); //将输入字符串转换成char

pResult=STRDUP_AtoT(ICTCLAS_ParagraphProcess(str,0)); //ICTCLAS中文分词函数调用
_CLDELETE_ARRAY(str);// 回收内存

pResult保存了分词后的结构,每个词是空格分隔,所以每次调用next()时,都从pResult里取下一个词,直接取完,则从Reader里读入下一段,继续分词,直接Reader已经到达尾部。代码如下

TCHAR word[LUCENE_MAX_WORD_LEN]={0}; //保存单个词的临时空间
while(pResult[end] !=_T(' ') && end <nCount) //查找空间,找到单词的结束位置
{
word[end-start]=pResult[end];
end++;
}
word[end-start]=0;
while(pResult[end]==_T(' ') && end<nCount) end++; //查找下一个词的开始位置
start=end;
token->setText(word); //输出到token

这个完成之后,ChineseTokenizer也就完成了。不过在这过程中,发现ICTCLAS分词速度并没想象中的快。不知道是不是因为这个是共享版的问题。


ChineseTokenizer完成后,接着就实现ChineseAnalyzer类。这个更加简单,只要完全模仿StandardAnalyzer就好了。ChineseAnalyzer是继承于Analyzer的,同样必须实现TokenStream* tokenStream(const TCHAR* fieldName, CL_NS(util)::Reader* reader)就可以了。这个只要生成一个ChineseTokenizer,返回就行了。代码如下:

TokenStream* ChineseAnalyzer::tokenStream(const TCHAR* fieldName, Reader* reader) {
TokenStream* ret= _CLNEW ChineseTokenizer(reader); //中文分词ChineseTokenizer
ret = _CLNEW LowerCaseFilter(ret,true); //英文大小变换,统一成小写
ret = _CLNEW StopFilter(ret,true, &stopSet); //停用词过滤,如“的”,“这”,“an”

return ret;
}

关于停用词的过滤,可以修改\src\CLucene\analysis\Analyzers.cpp里关于StopAnalyzer的定义。我个人加入了中文的停用词表

static const TCHAR* CHINESE_STOP_WORDS[];

在ChineseAnalyzer的构造函数里,将停用词表加入到映射中,先在ChineseAnalyzer里定义这个成员
CL_NS(util)::CLSetList<const TCHAR*> stopSet;用于保存停用词表,再加构造函数里加入下面这句:

StopFilter::fillStopTable( &stopSet,CL_NS(analysis)::StopAnalyzer::CHINESE_STOP_WORDS);

这样就完成了ChineseAnalyzer类。

接着就可以在索引里使用这个ChineseAnalyzer。
索引时需要生成ChineseAnalyzer的一个实例,将它传给IndexWriter。

查询时也是一样的道理,只不过是将它传给QueryParser。

这样就完成了CLucene加入ICTCLAS中文分词,但是发现ICTCLAS的分词速度不行,我只使用65K的文件就需要至少10秒的时间才完成。我正在寻找能替换ICTCLAS的中文分词。

这篇关于CLucene加入ICTCLAS中文分词的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/384033

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

vscode中文乱码问题,注释,终端,调试乱码一劳永逸版

忘记咋回事突然出现了乱码问题,很多方法都试了,注释乱码解决了,终端又乱码,调试窗口也乱码,最后经过本人不懈努力,终于全部解决了,现在分享给大家我的方法。 乱码的原因是各个地方用的编码格式不统一,所以把他们设成统一的utf8. 1.电脑的编码格式 开始-设置-时间和语言-语言和区域 管理语言设置-更改系统区域设置-勾选Bata版:使用utf8-确定-然后按指示重启 2.vscode

Solr 使用Facet分组过程中与分词的矛盾解决办法

对于一般查询而言  ,  分词和存储都是必要的  .  比如  CPU  类型  ”Intel  酷睿  2  双核  P7570”,  拆分成  ”Intel”,”  酷睿  ”,”P7570”  这样一些关键字并分别索引  ,  可能提供更好的搜索体验  .  但是如果将  CPU  作为 Facet  字段  ,  最好不进行分词  .  这样就造成了矛盾  ,  解决方法

解决Office Word不能切换中文输入

我们在使用WORD的时可能会经常碰到WORD中无法输入中文的情况。因为,虽然我们安装了搜狗输入法,但是到我们在WORD中使用搜狗的输入法的切换中英文的按键的时候会发现根本没有效果,无法将输入法切换成中文的。下面我就介绍一下如何在WORD中把搜狗输入法切换到中文。

sqlite不支持中文排序,采用java排序

方式一 不支持含有重复字段进行排序 /*** sqlite不支持中文排序,改用java排序* 根据指定的对象属性字段,排序对象集合,顺序* @param list* @param field* @return*/public static List sortListByField(List<?> list,String field){List temp = new ArrayList(

彻底解决win10系统Tomcat10控制台输出中文乱码

彻底解决Tomcat10控制台输出中文乱码 首先乱码问题的原因通俗的讲就是读的编码格式和写的解码格式不一致,比如最常见的两种中文编码UTF-8和GBK,UTF-8一个汉字占三个字节,GBK一个汉字占两个字节,所以当编码与解码格式不一致时,输出端当然无法识别这是啥,所以只能以乱码代替。 值得一提的是GBK不是国家标准编码,常用的国标有两,一个是GB2312,一个是GB18030 GB1

matplotlib中文乱码问题

在使用Matplotlib进行数据可视化的过程中,经常会遇到中文乱码的问题。显示乱码是由于编码问题导致的,而matplotlib 默认使用ASCII 编码,但是当使用pyplot时,是支持unicode编码的,只是默认字体是英文字体,导致中文无法正常显示,所以显示中文乱码。 文本使用系统默认字体、手动指定字体、使用字体管理器来解决。 一、系统默认字体(全局设置字体) 在Matplotlib中

Java实现Smartcn中文分词

新建一个Maven项目,修改pom.xml文件内容:注意版本的不同; <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-smartcn --><dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-analyzers

C++利用jsoncpp库实现写入和读取json文件(含中文处理)

C++利用jsoncpp库实现写入和读取json文件 1 jsoncpp常用类1.1 Json::Value1.2 Json::Reader1.3 Json::Writer 2 json文件3 写json文件3.1 linux存储结果3.2 windows存储结果 3 读json文件4 读json字符串参考文章 在C++中使用跨平台的开源库JsonCpp,实现json的序列化和反序列

解决IntelliJ IDEA 使用 TOMCAT 中文乱码问题

运行tomcat时,控制台乱码 1)打开Run/Debug Configuration,选择你的tomcat 2)然后在 Server > VM options 设置为 -Dfile.encoding=UTF-8 ,重启tomcat