Lucene文本解析过程

Lucene对文本解析是作为全文索引及全文检索的预处理形式出现的,因此在一般的Lucene文档中,这一部分都不是重点,往往一带而过,但是对于要建立基于文本的内容推荐引擎来说,却是相当关键的一步,因此有必要认真研究一下Lucene对文解析的过程。

Lucene对文本的解析对用户的接口是Analyzer的某个子类,Lucene内置了几个子类,但是对于英文来说StandardAnalyzer是最常用的一个子类,可以处理一般英文的文解析功能。但是对于汉字而言,Lucene提供了两个扩展包,一个是CJKAnalyzer和SmartChineseAnalyzer,其中SmartAnalyzer对处理中文分词非常适合,但是遗憾的是,该类将词典利用隐马可夫过程算法,集成在了算法里,这样的优点是减小了体积,并且安装方便,但是如果想向词库中添加单词就需要重新学习,不太方便。因此我们选择了MMSeg4j,这个开源的中文分词模块,这个开源软件的最大优点就可用户可扩展中文词库,非常方便,缺点是体积大加载慢。

首先通过一个简单的程序来看中文分词的使用:

Analyzer analyzer = null;
//analyzer = new StandardAnalyzer(Version.LUCENE_33);
//analyzer = new SimpleAnalyzer(Version.LUCENE_33);
analyzer = new MMSegAnalyzer();
TokenStream tokenStrm = analyzer.tokenStream("content", new StringReader(examples));

OffsetAttribute offsetAttr = tokenStrm.getAttribute(OffsetAttribute.class);
CharTermAttribute charTermAttr = tokenStrm.getAttribute(CharTermAttribute.class);
PositionIncrementAttribute posIncrAttr =
tokenStrm.addAttribute(PositionIncrementAttribute.class);
TypeAttribute typeAttr = tokenStrm.addAttribute(TypeAttribute.class);
String term = null;
int i = 0;
int len = 0;
char[] charBuf = null;
int termPos = 0;
int termIncr = 0;
try {
while (tokenStrm.incrementToken()) {
charBuf = charTermAttr.buffer();
termIncr = posIncrAttr.getPositionIncrement();
if (termIncr > 0) {
termPos += termIncr;
}
for (i=(charBuf.length - 1); i>=0; i--) {
if (charBuf[i] > 0) {
len = i + 1;
break;
}
}
//term = new String(charBuf, offsetAttr.startOffset(), offsetAttr.endOffset());
term = new String(charBuf, 0, offsetAttr.endOffset() - offsetAttr.startOffset());
System.out.print("[" + term + ":" + termPos + "/" + termIncr + ":" +
typeAttr.type() + ";" + offsetAttr.startOffset() + "-" + offsetAttr.endOffset() + "]  ");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

这里需要注意的是:

  • TermAttribute已经在Lucene的新版本中被标为过期,所以程序中使用CharTermAttribute来提取每个中文分词的信息
  • MMSegAnalyzer的分词效果在英文的条件下基本与Lucene内置的StandardAnalyzer相同

可以进行初步的中文分词之后,我们还需处理停止词去除,例如的、地、得、了、呀等语气词,还有就是添加同义词:第一种是完全意义上的同义词,如手机和移动电话,第二种是缩写与全称,如中国和中华人民共和国,第三种是中文和英文,如计算机和PC,第四种是各种专业词汇同义词,如药品名和学名,最后可能还有一些网络词语如神马和什么等。

在Lucene架构下,有两种实现方式,第一种是编写TokenFilter类来实现转换和添加,还有一种就是直接集成在相应的Analyzer中实现这些功能。如果像Lucene这样的开源软件,讲求系统的可扩展性的话,选择开发独立的TokenFilter较好,但是对于我们自己的项目,选择集成在Analyzer中将是更好的选择,这样可以提高程序执行效率,因为TokenFilter需要重新逐个过一遍所有的单词,效率比较低,而集成在Analyzer中可以保证在分解出单词的过程中就完成了各种分词操作,效率当然会提高了。

Lucene在文本解析中,首先会在Analyzer中调用Tokenizer,将文本分拆能最基本的单位,英文是单词,中文是单字或词组,我们的去除停止词和添加同义词可以放入Tokenizer中,将每个新拆分的单词进行处理,具体到我们所选用的MMSeg4j中文分词模块来说,就是需要在MMSegTokenizer类的incrementToken方法中,添加去除停止词和添加同义词:

if (0 == synonymCnt) {
clearAttributes();
Word word = mmSeg.next();
currWord = word;
if(word != null) {
// 去除截止词如的、地、得、了等

String wordStr = word.getString();

if (isStopWord(wordStr) {
return incrementToken();
}
if (hasSynonymWords(wordStr) {
synonymCnt = getSynonymWordsNum(wordStr);
}
//termAtt.setTermBuffer(word.getSen(), word.getWordOffset(), word.getLength()); 已过期API
offsetAtt.setOffset(word.getStartOffset(), word.getEndOffset());
charTermAttr.copyBuffer(word.getSen(), word.getWordOffset(), word.getLength());
posIncrAttr.setPositionIncrement(1);
typeAtt.setType(word.getType());
return true;
} else {
end();
return false;
}
} else {
String synonymWord = getSynonymWord(currWord.getString());

offsetAtt.setOffset(currWord.getStartOffset(), currWord.getEndOffset());
typeAtt.setType(currWord.getType());
charTermAttr.copyBuffer(t1, 0, t1.length);
posIncrAttr.setPositionIncrement(0);
synonymCnt--;
return true;
}

转载于:https://my.oschina.net/wangguolongnk/blog/108141

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值