- 浏览: 888719 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
一个优秀的IR system要做好的第一件事就是利用自然语言处理技术(NLP)对文本进行分析。其中分词是最基本的,其性能直接决定IR system的搜索精度和速度。因此,大型Web搜索引擎都有自己的分词工具。
Lucene3.0 的分析器由三个包组成:
(1) org.apache.lucene.analysis 是Lucene分析器的基本结构包。包含了分析器最底层的结构(Analyzer、Tokenizer、TokenFilter接口和抽象类),一些简单分析器的具体实现类(如SimpleAnayzer, StopAnalyzer),一些常用的分词器和过滤器(如LowerCaseTokenizer、LowerCaseFilter)。
(2) org.apache.lucene.analysis.standard 是Lucene标准分析器的实现包。其功能就是为了实现英文的标准分词。
(3) org.apache.lucene.analysis.tokenattribute 是分词后token的属性结构包。其实Lucene分词并不仅仅只是得到词语本身,而是要得到每个词语的多种信息(属性)。比如词语字符串、类型、位置信息、存储的时候元数据信息等等。
一、 Lucene的分析器结构
org.apache.lucene.analysis 是Lucene Analyzer底层结构包。主要包括Analyzer、Tokenizer和TokenFilter的接口规定。实际上,Lucene的Analyzer主要功能包括两个部分:(1)Tokenzier 分词器 (2)TokenFilter过滤器。
/** * Analyzer 定义了从文本中抽取词的一组规范。 * 首先要实现一个Tokenizer,这个类会把输入流中的字符串切分成原始的词元。 * 然后多个TokenFilter 就能够将这些词元规范化得到分词的结果 */ public abstract class Analyzer implements Closeable { //具体实现应该是要返回一个嵌套了分词器和过滤器的对象。 public abstract TokenStream tokenStream(String fieldName, Reader reader); //...... }
要实现一种Lucene的分析器(Analyzer),至少要实现一个分词器(Tokenizer)。对于特定语言来说,必要的过滤器(TokenFilter)也是不可缺少的。其中过滤器有很多种,主要可以用来对分词结果进行标准化。比如去停用词、转换大小写、英文的词干化(stemming)和词类归并 (lemmatization)等等。下面我们看看Tokenizer和TokenFilter的主要代码:
//Tokenizer public abstract class Tokenizer extends TokenStream { /**待分词的文本输入流 */ protected Reader input; /**无参构造器 */ protected Tokenizer() { } /** 带输入流的构造器*/ protected Tokenizer(Reader input) { this.input = CharReader.get(input); } /** 关闭输入流 */ @Override public void close() throws IOException { input.close(); } }
//TokenFilter public abstract class TokenFilter extends TokenStream { /** 待过滤的词元流 */ protected final TokenStream input; /** 构造器 */ protected TokenFilter(TokenStream input) { super(input); this.input = input; } /** 关闭流 */ @Override public void close() throws IOException { input.close(); } }
分词器和过滤器都是TokenStream的子类。而过滤器的构造参数需要的就是TokenStream。这是一种装饰者的模式设计,我们可以通过嵌套调用来达到不同的过滤目的。比如: new XTokenFilter(new YTokenFilter( new XTokenizer))。
相对于老版本的Lucene分词器,3.0版本的Lucene的Tokenizer多了一种构造器。
protected Tokenizer(AttributeSource source)
二、Lucene的标准分析器——StandardAnalyzer
org.apache.lucene.analysis.standard 包含了Lucene的标准分析器(StandardAnalyzer),它由标准分词器(StandardTokenizer)和标准过滤器(StandardFilter)构成。都只能处理英文。
StandardAnalyzer 部分源代码如下:
public class StandardAnalyzer extends Analyzer { /**英语停用词表*/ public static final Set<?> STOP_WORDS_SET = StopAnalyzer.ENGLISH_STOP_WORDS_SET; /**若干构造器*/ public StandardAnalyzer(Version matchVersion) { this(matchVersion, STOP_WORDS_SET); } /**分词并进行标准过滤、大小写过滤和停用词过滤*/ @Override public TokenStream tokenStream(String fieldName, Reader reader) { //构造一个标准分词器,并进行分词 StandardTokenizer tokenStream = new StandardTokenizer(matchVersion, reader); //设置分词后词元流的最大长度 tokenStream.setMaxTokenLength(maxTokenLength); //进行标准过滤 TokenStream result = new StandardFilter(tokenStream); //进行大小写过滤 result = new LowerCaseFilter(result); //进行停用词过滤 result = new StopFilter(enableStopPositionIncrements, result, stopSet); return result; } }
StandardAnalyzer是Lucene索引建立和检索索引时都需要使用的分析器,tokenStream方法的作用就是对输入流reader先进行分词,再进行一系列的过滤。
标准分词器:StandardTokenizer
public final class StandardTokenizer extends Tokenizer { /**JFlex扫描器*/ private final StandardTokenizerImpl scanner; /**从输入流字串中解析出的词元的各种信息*/ private TermAttribute termAtt; //词元的内容,如"tearcher" "xy12@yahoo.com" "1421" private OffsetAttribute offsetAtt; //词元的首字母和尾字母在文本中的位置信息 private PositionIncrementAttribute posIncrAtt; //当前词元在TokenStream中相对于前一个token的位置,用于短语搜索 private TypeAttribute typeAtt; //词元所属的类别,,如<ALPHANUM>、<EMAIL>、<NUM> //标准分词器构造器,并用JFlex对象解析输入流 public StandardTokenizer(Version matchVersion, Reader input) { super(); this.scanner = new StandardTokenizerImpl(input); init(input, matchVersion); } //初始化词元的属性信息 private void init(Reader input, Version matchVersion) { if (matchVersion.onOrAfter(Version.LUCENE_24)) { replaceInvalidAcronym = true; } else { replaceInvalidAcronym = false; } this.input = input; termAtt = addAttribute(TermAttribute.class); offsetAtt = addAttribute(OffsetAttribute.class); posIncrAtt = addAttribute(PositionIncrementAttribute.class); typeAtt = addAttribute(TypeAttribute.class); } //将JFlex扫描后的匹配结果按词元的不同属性存储 //比如当前词元是I'm 则将I'm存储到TermAttribute中,而<APOSTROPHE>则存放到TypeAttribute中。 @Override public final boolean incrementToken() throws IOException { clearAttributes(); int posIncr = 1; while(true) { //通过JFlex扫描器scanner取得与规则相匹配的当前词元,否则返回-1 int tokenType = scanner.getNextToken(); if (tokenType == StandardTokenizerImpl.YYEOF) { return false; } //scanner.yylength() 是当前词元的长度,maxTokenLength是词元允许的最大长度,值为255 if (scanner.yylength() <= maxTokenLength) { posIncrAtt.setPositionIncrement(posIncr); //将当前词元字串储记录在TermAttribute属性中,比如“I'm” scanner.getText(termAtt); //得到当前词元首字母在整个文本内容中的位置 final int start = scanner.yychar(); //将当前词元的位置信息(开始位置,结束位置)记录在OffsetAttribute属性中 offsetAtt.setOffset(correctOffset(start), correctOffset(start+termAtt.termLength())); //确定当前词元的类别信息,并记录在TypeAttribute属性中 if (tokenType == StandardTokenizerImpl.ACRONYM_DEP) { if (replaceInvalidAcronym) { typeAtt.setType(StandardTokenizerImpl.TOKEN_TYPES[StandardTokenizerImpl.HOST]); termAtt.setTermLength(termAtt.termLength() - 1); } else { typeAtt.setType(StandardTokenizerImpl.TOKEN_TYPES[StandardTokenizerImpl.ACRONYM]); } } else { typeAtt.setType(StandardTokenizerImpl.TOKEN_TYPES[tokenType]); } return true; } else posIncr++; } } }
Lucene的英文分词器使用了JFlex的词法扫描方法。其具体实现在初始化StandardTokenizerImpl类时,通过调用类中的静态方法和StandardTokenizerImpl.jflex词法描述文件来一起解析待分词的输入流。并将最后扫描出来的词语分成 <ALPHANUM>、<APOSTROPHE>、<ACRONYM>、<COMPANY>、<EMAIL>、<HOST>、<NUM>、<CJ>、 <ACRONYM_DEP>九大类。这一过程和Java编译器的词法分析程序对Java程序的关键字、变量名等进行解析是一样的。因此想要了解JFlex,必须知道编译原理的相关知识,这里就不展开了(因为我也不知道)。
scanner是StandardTokenizerImpl类初始化的对象,这个对象里存储了扫描输入流字串得到的词元信息(词元的内容、长度、所属的类别、所在位置等)。相对于较早的版本,Lucene 3.0在这里有很大的变化。它没有用next()方法直接得到TokenStream的下一个词元内容,而是使用incrementToken()方法将每一个scanner.getNextToken()的各种词元信息保存在不同类型的Attribute里面,比如TermAttribute用于保存词元的内容,TyteAttribute用于保存词元的类型。
标准过滤器:StandardFilter
public final class StandardFilter extends TokenFilter { /** * 去除词语末尾的“'s” 如 it's-> it * 去除缩略语中的“.” 如U.S.A -> USA */ @Override public final boolean incrementToken() throws java.io.IOException { if (!input.incrementToken()) { return false; } char[] buffer = termAtt.termBuffer(); final int bufferLength = termAtt.termLength(); final String type = typeAtt.type(); if (type == APOSTROPHE_TYPE && bufferLength >= 2 && buffer[bufferLength-2] == '\'' && (buffer[bufferLength-1] == 's' || buffer[bufferLength-1] == 'S')) { termAtt.setTermLength(bufferLength - 2); } else if (type == ACRONYM_TYPE) { int upto = 0; for(int i=0;i<bufferLength;i++) { char c = buffer[i]; if (c != '.') buffer[upto++] = c; } termAtt.setTermLength(upto); } return true; } }
三、token的属性结构Attribute
首先我们用下面的代码来看看打印标准分词器的运行结果
class StandardTest{ public static void main(String[] args) throws IOException{ //输入流 StringReader s=new StringReader(new String("I'm a student. these are apples")); //标准分词 TokenStream tokenStream = new StandardTokenizer(Version.LUCENE_CURRENT, s); //标准过滤 tokenStream=new StandardFilter(tokenStream); //大小写过滤 tokenStream=new LowerCaseFilter(tokenStream); TermAttribute termAtt=(TermAttribute)tokenStream.getAttribute(TermAttribute.class); TypeAttribute typeAtt=(TypeAttribute)tokenStream.getAttribute(TypeAttribute.class); OffsetAttribute offsetAtt=(OffsetAttribute)tokenStream.getAttribute(OffsetAttribute.class); PositionIncrementAttribute posAtt=(PositionIncrementAttribute)tokenStream.getAttribute(PositionIncrementAttribute.class); System.out.println("termAtt typeAtt offsetAtt posAtt"); while (tokenStream.incrementToken()) { System.out.println(termAtt.term()+" "+typeAtt.type()+" ("+offsetAtt.startOffset()+","+offsetAtt.endOffset()+") "+posAtt.getPositionIncrement()); } } }
打印结果:
termAtt | typeAtt | offsetAtt | posAtt |
i'm | <APOSTROPHE> | (0,3) | 1 |
a | <ALPHANUM> | (4,5) | 1 |
student | <ALPHANUM> | (6,13) | 1 |
these | <ALPHANUM> | (15,20) | 1 |
are | <ALPHANUM> | (21,34) | 1 |
apples | <ALPHANUM> | (25,31) | 1 |
在前面讲 StandardTokenizer的的时候,我们已经谈到了token的这四种属性。在这里我们再次强调一下这些Lucene的基础知识。
Lucene 3.0之后,TokenStream中的每一个token不再用next()方法返回,而是采用了incrementToken()方法(具体参见上面)。每调用一次incrementToken(),都会得到token的四种属性信息(org.apache.lucene.analysis.tokenattributes包中):
如上例:
原文本:I'm a student. these are apples
TokenSteam: [1: I'm ] [2:a] [3:student] [4:these] [5:are ] [6:apples]
(1) TermAttribute: 表示token的字符串信息。比如"I'm"
(2) TypeAttribute: 表示token的类别信息(在上面讲到)。比如 I'm 就属于<APOSTROPHE>,有撇号的类型
(3) OffsetAttribute:表示token的首字母和尾字母在原文本中的位置。比如 I'm 的位置信息就是(0,3)
(4) PositionIncrementAttribute:这个有点特殊,它表示tokenStream中的当前token与前一个token在实际的原文本中相隔的词语数量。
比如: 在tokenStream中[2:a] 的前一个token是[1: I'm ] ,它们在原文本中相隔的词语数是1,则token="a"的PositionIncrementAttribute值为1。如果token是原文本中的第一个词,则默认值为1。因此上面例子的PositionIncrementAttribute结果就全是1了。
如果我们使用停用词表来进行过滤之后的话:TokenSteam就会变成: [1: I'm ] [2:student] [3:apples]这时student的PositionIncrementAttribute值就不会再是1,而是与[1: I'm ]在原文本中相隔词语数量=2。而apples则变成了5。
那么这个属性有什么用呢,用处很大的。加入我们想搜索一个短语student apples(假如有这个短语)。很显然,用户是要搜索出student apples紧挨着出现的文档。这个时候我们找到了某一篇文档(比如上面例子的字符串)都含有student apples。但是由于apples的PositionIncrementAttribute值是5,说明肯定没有紧挨着。怎么样,用处很大吧。轻而易举的解决了短语搜索的难题哦。
其实还有两种:PayloadAttribute和FlagsAttribute。我暂时还不知道他们的具体作用
评论
过滤后TokenSteam: [1: I'm ] [2:student] [3:apples]
按照定义:PositionIncrementAttribute表示tokenStream中的当前token与前一个token在实际的原文本中相隔的词语数量
student对应的PositionIncrementAttribute为2
那么apples对应的PositionIncrementAttribute不应该是3么?求解释...
发表评论
-
【Lucene3.0 初窥】索引文件格式(2):文件结构总体框架
2010-05-02 16:44 4038Lucene使用文件扩展名标识不同的索引文件。如.fnm文件存 ... -
【Lucene3.0 初窥】索引文件格式(1):预备知识
2010-05-02 16:26 3952注意,本专题内容参见《http://lucene.apache ... -
【Lucene3.0 初窥】索引文件格式(5):posting数据[.frq/.prx]
2010-05-02 12:34 3809★ .frq 词语频 ... -
【Lucene3.0 初窥】索引文件格式(4):dictionary数据[.tii/.tis]
2010-04-30 10:57 3520Terms数据 磁盘文件存储细节 从这篇开始 ... -
【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]
2010-04-23 15:12 5111注意:以下文章是参见h ... -
【Lucene3.0 初窥】索引创建(6):关闭IndexWriter
2010-04-23 15:09 42861.5 IndexWriter的关闭细节 In ... -
【Lucene3.0 初窥】索引创建(4):DocumentWriter 处理流程三
2010-04-15 15:36 3549上接《索引创建(3):DocumentWriter 处理流程二 ... -
【Lucene3.0 初窥】索引创建(5):索引数据池及内存数据细节
2010-04-13 13:50 3723上接《索引创建 (2):DocumentWriter处理流程 ... -
【Lucene3.0 初窥】索引创建(3):DocumentWriter 处理流程二
2010-04-10 10:27 4057上接《索引创建(2):DocumentWriter处理流 ... -
【Lucene3.0 初窥】索引创建(2):DocumentWriter 处理流程一
2010-04-08 21:55 3648上接《索引创建(1): IndexWriter索引器》 ... -
【Lucene3.0 初窥】索引创建(1):IndexWriter索引器
2010-04-07 19:11 4822《Lucene索引创建》系列文章将从源代码出发,详细揭示Luc ... -
【Lucene3.0 初窥】数据源内存组织结构—Document/Field
2010-04-07 16:45 3773在检索数据的时候,我们很希望可以检索出数据源的各种信息。就比如 ... -
《Introduce to IR》索引创建
2010-04-03 10:41 3374该系列文章是《An Introduce to Inform ... -
《Introduce to IR》布尔检索模型
2010-03-18 09:33 5487该系列文章是《An Introduce to Informat ... -
【Lucene3.0 初窥】Lucene体系结构概述
2010-03-05 11:37 4003Lucene 的基本原理与《 ... -
【Lucene3.0 初窥】全文检索的基本原理
2010-03-04 16:01 5614全文转载:http://blog.csdn.net/forfu ...
相关推荐
1.IKAnalyzer3.0介绍 IKAnalyzer是一个开源的,基于java... * 针对Lucene全文检索优化的查询分析器IKQueryParser(作者吐血推荐);采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。
A Chinese Analyzer that utilizes HMM. 基于隐马尔科夫模型的中文分析器。
从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词...针对Lucene全文检索优化的查询分析器IKQueryParser(作者吐血推荐);采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。
IKAnalyzer是一个开源的,基于Java语言开发的轻量级的中文分词语言包,它是以Lucene为应用主体,结合词典分词和文法分析算法的中文词组组件。从3.0版本开始,IK发展为面向java的公用分词组件,独立Lucene项目,同时...
IK Analyzer是一个结合词典分词和文法分词的中文分词开源工具包。它使用了全新的正向迭代最细粒度切分算法。可以结合lucene4.0使用
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出了3个大版本。...针对Lucene全文检索优化的查询分析器IKQueryParser(作者吐血推荐);
使用Lucene 分词器Jsceg IkAnalyzer jar包 ucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了...
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出了3个大版本。...针对Lucene全文检索优化的查询分析器IKQueryParser(作者吐血推荐);
IKAnalyzer继承Lucene的Analyzer抽象类,使用IKAnalyzer和Lucene自带的分析器方法一样,将Analyzer测试代码改为IKAnalyzer测试中文分词效果。 如果使用中文分词器ik-analyzer,就需要在索引和搜索程序中使用一致的...
从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。此...
****关键内容: 此中文分词器基于lucene5.5.5和jdk1.7打包发布***** IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它...
从 3.0 版本开始,IK 发展为面向 Java 的公用分词组件,独立亍Lucene 项目,同时提供了对 Lucene 的默认优化实现。在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK分词器从单纯的词典分词向模拟语义...
IK Analyzer 是一个开源的,基于java 语言开发的轻量级的中文分词工具包。...Analyzer 3.0 则发展为面向Java 的公用分词组件,独立于Lucene 项目,同时提供了对 Lucene 的默认优化实现。 该资源包涵用户手册,API及包
IKAnalyzer 分词器jar IKAnalyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。...新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。
IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。...从3.0版本开 始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了
IK 发展为面向 Java 的公用分词组件,独立于 Lucene 项目,同时提供了对 Lucene 的默认 优化实现。在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯 的词典分词向模拟语义分词衍化。
从3.0版本开 始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词 歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。 ...
IKAnalyzer是一款中文分析器,应用于Solr搜索服务当中。
捷巴-lucene分析 jieba使用的lucene中文分析器和Tokenizer( )。