Analysis包分析

Analysis包分析

算法和数据结构分析:

由于Analysis包比较简单,不详述了!

算法:基于机械分词 1-gram,2-gram,HMM(如果使用ICTCLAS接口的话)

数据结构:部分源码用到了Set ,HashTable,HashMap

认真理解Token

Lucene中的Analysis包专门用于完成对于索引文件的分词.Lucene中的Token是一个非常重要的概念.

看一下其源码实现:

public   final   class  Token  {

  String termText;                        
// the text of the term

  
int startOffset;                           // start in source text

  
int endOffset;                            // end in source text

  String type 
= "word";                        // lexical type

  
private int positionIncrement = 1;

  
public Token(String text, int start, int end)

  
public Token(String text, int start, int end, String typ)

  
public void setPositionIncrement(int positionIncrement)

  
public int getPositionIncrement() return positionIncrement; }

  
public final String termText() return termText; }

  
public final int startOffset() return startOffset; }

public void setStartOffset(int givenStartOffset)

  
public final int endOffset() return endOffset; }

public void setEndOffset(int givenEndOffset)

  
public final String type() return type; }

  
public String toString()

 }


下面编一段代码来看一下

TestToken.java

package  org.apache.lucene.analysis.test;

import  org.apache.lucene.analysis. * ;

import  org.apache.lucene.analysis.standard.StandardAnalyzer;

import  java.io. * ;

public   class  TestToken

{

  
public static void main(String[] args)

  
{

    String string 
= new String("我爱天大,但我更爱中国");

//Analyzer analyzer = new StandardAnalyzer();

Analyzer analyzer 
= new TjuChineseAnalyzer();

//Analyzer analyzer= new StopAnalyzer();

    TokenStream ts 
= analyzer.tokenStream("dummy",new StringReader(string));

    Token token;

    
try

    
{

      
int n=0;

      
while ( (token = ts.next()) != null)

      
{

        System.out.println((n
++)+"->"+token.toString());

      }


    }


    
catch(IOException ioe)

    
{

      ioe.printStackTrace();

    }


 

  }


}


注意看其结果如下所示

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,0,1,<CJK>,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,1,2,<CJK>,1)

2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,2,3,<CJK>,1)

3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,3,4,<CJK>,1)

4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,5,6,<CJK>,1)

5->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,6,7,<CJK>,1)

6->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,7,8,<CJK>,1)

7->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,8,9,<CJK>,1)

8->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,9,10,<CJK>,1)

9->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,10,11,<CJK>,1)

注意:其中”,”StandardAnalyzer给过滤掉了,所以大家注意第4Token直接startOffset5开始.

如果改用StopAnalyzer()

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(我爱天大,0,4,word,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(但我更爱中国,5,11,word,1)

改用TjuChineseAnalyzer(我写的,下文会讲到如何去写)

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,3,4,word,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(天大,6,8,word,1)

2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,19,20,word,1)

3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,22,23,word,1)

4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(中国,25,27,word,1)

讲明白了Token,咱们来看以下其他的东西

一个TokenStream是用来走访Tokeniterator(迭代器)

看一下其源代码:

public   abstract   class  TokenStream  {

  
public abstract Token next() throws IOException;

  
public void close() throws IOException {}

}


一个Tokenizeris-a TokenStream(派生自TokenStream),其输入为Reader

看一下其源码如下:

public   abstract   class  Tokenizer  extends  TokenStream  {

  
protected Reader input;

  
protected Tokenizer() {}

  
protected Tokenizer(Reader input) {

    
this.input = input;

  }


  
public void close() throws IOException {

    input.close();

  }


}


一个TokenFilter is–a TokenStream(派生自TokenStream),其义如名就是用来完成对TokenStream的过滤操作,譬如

StopWords,将Token变为小写等。

源码如下:

public   abstract   class  TokenFilter  extends  TokenStream  {

  
protected TokenStream input;

  
protected TokenFilter() {}

  
protected TokenFilter(TokenStream input) {

    
this.input = input;

  }


  
public void close() throws IOException {

    input.close();

  }


}


一个Analyzer就是一个TokenStream工厂

看一下其源码就:

public   abstract   class  Analyzer 

  
public TokenStream tokenStream(String fieldName, Reader reader)

  
{

         
return tokenStream(reader);

  }


  
public TokenStream tokenStream(Reader reader)

  
{

         
return tokenStream(null, reader);

  }


}


好,现在咱们来看一下LuceneAnalysis包下面的各个类文件都是用来干什么的。按照字典排序。

Analysis包中的源码详解

Analyzer.java  上文已经讲过。

CharTokenizer.java  此类为简单一个抽象类,用来对基于字符的进行简单分词(tokenizer

LetterTokenizer.java两个非字符之间的字符串定义为token(举例来说英文单词由空白隔开,那个两个空白之间的字符串即被定义为一个token。备注:对于绝大多数欧洲语言来说,这个类工作效能很好。当时对于不用空白符分割的亚洲语言,效能极差(譬如中日韩)。)

LowerCaseFilter.java is-a TokenFilter用于将字母小写化

LowerCaseTokenizer is-a Tokenizer功能上等价于LetterTokenizerLowerCaseFilter

PerFieldAnalyzerWrapper是一个Analyzer,因为继承自Analyzer当不同的域(Field)需要不同的语言分析器(Analyzer)时,这个Analyzer就派上了用场。使用成员函数addAnalyzer可以增加一个非缺省的基于某个Fieldanalyzer。很少使用。

PorterStemFilter.java使用词干抽取算法对每一个token流进行词干抽取。

PorterStemmer.java  有名的P-stemming算法

SimpleAnalyzer.java

StopAnalyzer.java   具有过滤停用词的功能

StopFilter.java     StopFilter为一个Filter,主要用于从token流中去除StopWords

Token.java       上面已讲.

TokenFilter.java   上面已经讲了

Tokenizer.java     上面已经讲了

TokenStream.java   上面已经讲了

WhitespaceAnalyzer.java

WhitespaceTokenizer.java 只是按照space区分Token.

 

由于Luceneanalyisis包下的Standard包下的StandardAnalyzer()功能很强大,而且支持CJK分词,我们简要说一下.

此包下的文件是有StandardTokenizer.jj经过javac命令生成的.由于是机器自动生成的代码,可能可读性很差,想了解的话好好看看那个StandardTokenizer.jj文件就会比较明了了.

Lucene常用的Analyzer功能概述.

WhitespaceAnalyzer:仅仅是去除空格,对字符没有lowcase,不支持中文

SimpleAnalyzer:功能强于WhitespaceAnalyzer,将除去letter之外的符号全部过滤掉,并且将所有的字符lowcase,不支持中文

StopAnalyzer:StopAnalyzer的功能超越了SimpleAnalyzer,在SimpleAnalyzer的基础上
   
增加了去除StopWords的功能,不支持中文

StandardAnalyzer:英文的处理能力同于StopAnalyzer.支持中文采用的方法为单字切分.

ChineseAnalyzer:来自于Lucenesand box.性能类似于StandardAnalyzer,缺点是不支持中英文混和分词.

CJKAnalyzer:chedong写的CJKAnalyzer的功能在英文处理上的功能和StandardAnalyzer相同
   
但是在汉语的分词上,不能过滤掉标点符号,即使用二元切分

TjuChineseAnalyzer:我写的,功能最为强大.TjuChineseAnlyzer的功能相当强大,在中文分词方面由于其调用的为ICTCLASjava接口.所以其在中文方面性能上同与ICTCLAS.其在英文分词上采用了LuceneStopAnalyzer,可以去除 stopWords,而且可以不区分大小写,过滤掉各类标点符号.

各个Analyzer的功能已经比较介绍完毕了,现在咱们应该学写Analyzer,如何diy自己的analyzer??

如何DIY一个Analyzer

咱们写一个Analyzer,要求有一下功能

(1)    可以处理中文和英文,对于中文实现的是单字切分,对于英文实现的是以空格切分.

(2)    对于英文部分要进行小写化.

(3)    具有过滤功能,可以人工设定StopWords列表.如果不是人工设定,系统会给出默认的StopWords列表.

(4)    使用P-stemming算法对于英文部分进行词缀处理.

代码如下:

public   final   class  DiyAnalyzer

    
extends  Analyzer

{

  
private Set stopWords;

  
public static final String[] CHINESE_ENGLISH_STOP_WORDS =

      
{

      
"a""an""and""are""as""at""be""but""by",

      
"for""if""in""into""is""it",

      
"no""not""of""on""or""s""such",

      
"t""that""the""their""then""there""these",

      
"they""this""to""was""will""with",

      
"""我们"

  }
;

  
public DiyAnalyzer()

  
{

    
this.stopWords=StopFilter.makeStopSet(CHINESE_ENGLISH_STOP_WORDS);

  }


 

  
public DiyAnalyzer(String[] stopWordList)

  
{

    
this.stopWords=StopFilter.makeStopSet(stopWordList);

  }


 

  
public TokenStream tokenStream(String fieldName, Reader reader)

  
{

    TokenStream result 
= new StandardTokenizer(reader);

    result 
= new LowerCaseFilter(result);

 

    result 
= new StopFilter(result, stopWords);

    result 
= new PorterStemFilter(result);

    
return result;

  }


 

  
public static void main(String[] args)

  
{

    
//好像英文的结束符号标点.,StandardAnalyzer不能识别

    String string 
= new String("我爱中国,我爱天津大学!I love China!Tianjin  is a City");

    Analyzer analyzer 
= new DiyAnalyzer();

    TokenStream ts 
= analyzer.tokenStream("dummy"new StringReader(string));

    Token token;

    
try

    
{

      
while ( (token = ts.next()) != null)

      
{

        System.out.println(token.toString());

      }


    }


    
catch (IOException ioe)

    
{

      ioe.printStackTrace();

    }


  }


}


可以看见其后的结果如下:

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,1,2,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,2,3,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,3,4,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,6,7,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,7,8,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,8,9,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,9,10,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,10,11,<CJK>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(i,12,13,<ALPHANUM>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(love,14,18,<ALPHANUM>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(china,19,24,<ALPHANUM>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(tianjin,25,32,<ALPHANUM>,1)

Token's (termText,startOffset,endOffset,type,positionIncrement) is:(citi,39,43,<ALPHANUM>,1)

 

到此为止这个简单的但是功能强大的分词器就写完了,下面咱们可以尝试写一个功能更强大的分词器.

如何DIY一个功能更加强大Analyzer

譬如你有词典,然后你根据正向最大匹配法或者逆向最大匹配法写了一个分词方法,却想在Lucene中应用,很简单

你只要把他们包装成LuceneTokenStream就好了.下边我以调用中科院写的ICTCLAS接口为例,进行演示.你去中科院

网站可以拿到此接口的free版本,谁叫你没钱呢,有钱,你就可以购买了.哈哈

,由于ICTCLAS进行分词之后,Java,中间会以两个空格隔开!too easy,我们直接使用继承Lucene

WhiteSpaceTokenizer就好了.

所以TjuChineseTokenizer 看起来像是这样.

public   class  TjuChineseTokenizer  extends  WhitespaceTokenizer

{

  
public TjuChineseTokenizer(Reader readerInput)

  
{

    
super(readerInput);

  }


}


TjuChineseAnalyzer看起来象是这样

public   final   class  TjuChineseAnalyzer

    
extends  Analyzer

{

  
private Set stopWords;

 

  
/** An array containing some common English words that are not usually useful

    for searching. 
*/


  
/*

     public static final String[] CHINESE_ENGLISH_STOP_WORDS =

      {

      "a", "an", "and", "are", "as", "at", "be", "but", "by",

      "for", "if", "in", "into", "is", "it",

      "no", "not", "of", "on", "or", "s", "such",

      "t", "that", "the", "their", "then", "there", "these",

      "they", "this", "to", "was", "will", "with",

      "我", "我们"

     };

   
*/


  
/** Builds an analyzer which removes words in ENGLISH_STOP_WORDS. */

  
public TjuChineseAnalyzer()

  
{

    stopWords 
= StopFilter.makeStopSet(StopWords.SMART_CHINESE_ENGLISH_STOP_WORDS);

  }


 

  
/** Builds an analyzer which removes words in the provided array. */

  
//提供独自的stopwords

  
public TjuChineseAnalyzer(String[] stopWords)

  
{

    
this.stopWords = StopFilter.makeStopSet(stopWords);

  }


 

  
/** Filters LowerCaseTokenizer with StopFilter. */

  
public TokenStream tokenStream(String fieldName, Reader reader)

  
{

    
try

    
{

      ICTCLAS splitWord 
= new ICTCLAS();

      String inputString 
= FileIO.readerToString(reader);

      
//分词中间加入了空格

      String resultString 
= splitWord.paragraphProcess(inputString);

      System.out.println(resultString);

      TokenStream result 
= new TjuChineseTokenizer(new StringReader(resultString));

 

      result 
= new LowerCaseFilter(result);

      
//使用stopWords进行过滤

     result 
= new StopFilter(result, stopWords);

      
//使用p-stemming算法进行过滤

     result 
= new PorterStemFilter(result);

      
return result;

 

    }


    
catch (IOException e)

    
{

      System.out.println(
"转换出错");

      
return null;

    }


  }


 

  
public static void main(String[] args)

  
{

    String string 
= "我爱中国人民";

    Analyzer analyzer 
= new TjuChineseAnalyzer();

    TokenStream ts 
= analyzer.tokenStream("dummy"new StringReader(string));

    Token token;

    System.out.println(
"Tokens:");

    
try

    
{

      
int n=0;

      
while ( (token = ts.next()) != null)

      
{

        System.out.println((n
++)+"->"+token.toString());

      }


    }


    
catch (IOException ioe)

    
{

     ioe.printStackTrace();

    }


  }


}


对于此程序的输出接口可以看一下

0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(,3,4,word,1)

1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(中国,6,8,word,1)

2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(人民,10,12,word,1)

 

OK,经过这样一番讲解,你已经对LuceneAnalysis包认识的比较好了,当然如果你想更加了解,还是认真读读源码才好,

呵呵,源码说明一切!

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值