lucene 中文分词

内容提要:以ChineseAnalyzer为例,简单讲讲lucene分析器,也就是analyzer的分析过程

一:分析器原理

语料——>过滤器过滤——>tokeniner分词器分词——>词元——>放进字典(记录词元和位置信息)

二:代码分析

1:一共有5个类,第一个是ChineseAnalyzer分析器类,还有ChineseFilter过滤器类和它的工厂类,和ChineseTokenizer类和它的工厂类


2:ChineseAnalyzer类

[java]  view plain copy
  1. public final class ChineseAnalyzer extends Analyzer {  
  2.   
  3.     @Override  
  4.     protected TokenStreamComponents createComponents(String fieldName,  
  5.         Reader reader) {  
  6.       final Tokenizer source = new ChineseTokenizer(reader);//new一个tokenizer  
  7.       return new TokenStreamComponents(source, new ChineseFilter(source));//把tokonizer和过滤器放入语汇流处理器组建中  
  8.     }  
  9. }  

3:ChineseFilter类,默认按照空格来切割文档字词,主要处理停用词,和把英文字符长度为1的去掉

[java]  view plain copy
  1. public final class ChineseFilter extends TokenFilter {  
  2.   
  3.   
  4.     // Only English now, Chinese to be added later.停用词,可以添加在这里  
  5.     public static final String[] STOP_WORDS = {  
  6.     "and""are""as""at""be""but""by",  
  7.     "for""if""in""into""is""it",  
  8.     "no""not""of""on""or""such",  
  9.     "that""the""their""then""there""these",  
  10.     "they""this""to""was""will""with"  
  11.     };  
  12.   
  13.   
  14.     private CharArraySet stopTable;  
  15.   
  16.     private CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);  
  17.       
  18.     public ChineseFilter(TokenStream in) {  
  19.         super(in);  
  20.   
  21.         stopTable = new CharArraySet(Version.LUCENE_CURRENT, Arrays.asList(STOP_WORDS), false);  
  22.     }  
  23.   
  24.     @Override  
  25.     public boolean incrementToken() throws IOException {  
  26.   
  27.         while (input.incrementToken()) {  
  28.             char text[] = termAtt.buffer();//以空格为截断符截取出来的的字符数组  
  29.             int termLength = termAtt.length();  
  30. //过滤器的主要功能,字符是先按照空格截取后的字符数组,先判断是不是在停用词里面,然后判断是不是英文字母,在判断是不是其他字符  
  31.           // why not key off token type here assuming ChineseTokenizer comes first?  
  32.             if (!stopTable.contains(text, 0, termLength)) {//是不是在停用词里面  
  33.                 switch (Character.getType(text[0])) {  
  34.   
  35.                 case Character.LOWERCASE_LETTER://是不是引文字母  
  36.                 case Character.UPPERCASE_LETTER:  
  37.   
  38.                     // English word/token should larger than 1 character.  
  39.                     if (termLength>1) {//要是英文字母,且长度大于1才回返回给语汇处理器  
  40.                         return true;  
  41.                     }  
  42.                     break;  
  43.                 case Character.OTHER_LETTER://要是其他字符,直接返回  
  44.   
  45.                     // One Chinese character as one Chinese word.  
  46.                     // Chinese word extraction to be added later here.  
  47.   
  48.                     return true;  
  49.                 }  
  50.   
  51.             }  
  52.   
  53.         }  
  54.         return false;  
  55.     }  
  56.   
  57. }  
4:ChineseTokenizer类,是处理分词的

[java]  view plain copy
  1. public final class ChineseTokenizer extends Tokenizer {  
  2.     public ChineseTokenizer(Reader in) {  
  3.       super(in);  
  4.     }  
  5.     public ChineseTokenizer(AttributeSource source, Reader in) {  
  6.       super(source, in);  
  7.     }  
  8.     public ChineseTokenizer(AttributeFactory factory, Reader in) {  
  9.       super(factory, in);  
  10.     }  
  11.          
  12.     private int offset = 0, bufferIndex=0, dataLen=0;  
  13.     private final static int MAX_WORD_LEN = 255;  
  14.     private final static int IO_BUFFER_SIZE = 1024;  
  15.     private final char[] buffer = new char[MAX_WORD_LEN];  
  16.     private final char[] ioBuffer = new char[IO_BUFFER_SIZE];  
  17.   
  18.   
  19.     private int length;  
  20.     private int start;  
  21. //处理后的词元写进这两个属性,一个记录词元,一个是记录位置信息  
  22.     private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);//记录词元  
  23.     private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);//记录位置信息  
  24.     <span style="white-space:pre">  </span>//本地的一个写缓冲区,要是英文就先写到这里,写完一个英文单词再写到termAtt  
  25.     private final void push(char c) {  
  26.   
  27.         if (length == 0) start = offset-1;            // start of token  
  28.         buffer[length++] = Character.toLowerCase(c);  // buffer it  
  29.   
  30.     }  
  31. //把词元和词元的位置信息写到字典,返回true是表示还有词需要继续处理,返回false表示此次输入的文档处理完毕  
  32.     private final boolean flush() {  
  33. //length是指写入词典的词元的长度  
  34.         if (length>0) {  
  35.             //System.out.println(new String(buffer, 0,  
  36.             //length));  
  37.           termAtt.copyBuffer(buffer, 0, length);  
  38.           offsetAtt.setOffset(correctOffset(start), correctOffset(start+length));  
  39.           return true;  
  40.         }  
  41.         else  
  42.             return false;  
  43.     }  
  44.   
  45.     @Override  
  46.     public boolean incrementToken() throws IOException {  
  47.         clearAttributes();  
  48. //写完一个词元后,长度清零,新词元的起始位置从上一个词元的最后位置开始  
  49.         length = 0;//重置length  
  50.         start = offset;//把上一次的偏移量赋值成这一次的起始值  
  51.   
  52.   
  53.         while (true) {  
  54.   
  55.             final char c;  
  56.             offset++;  
  57. //将输入流ioBuffer读出来,当bufferIndex>=dataLen的时候,也就是一个输入流被处理完的时候  
  58. //再读ioBuffer,dateLen就会等于-1,也就是input.read(ioBuffer)=-1  
  59.            if (bufferIndex >= dataLen) {  
  60.                  dataLen = input.read(ioBuffer);  
  61.                 bufferIndex = 0;  
  62.             }  
  63. //如果dataLen等于-1,length是等于0的,进入flush,会直接返回false,就是该次输入的文档分析结束  
  64.            if (dataLen == -1){  
  65.                 offset--;   
  66.                 return flush();  
  67.             }else c = ioBuffer[bufferIndex++];//取出输入流的字符   
  68.                 
  69.             switch(Character.getType(c)) {//如果是数字和字母,就写入本地缓存,然后处理下一个字符,如果等于最大长度了,直接写入   
  70.             case Character.DECIMAL_DIGIT_NUMBER:   
  71.             case Character.LOWERCASE_LETTER:   
  72.             case Character.UPPERCASE_LETTER:   
  73.                  push(c);   
  74.                  if (length == MAX_WORD_LEN)return flush();   
  75.                  break;  
  76. //如果是其他符号,要是有本地缓存,就先写本地缓存,再后退一次(避免数字,字母和其他字符变成一个词元写入),要是没有本地缓存就直接写入,  
  77. //保证了数字和字母结束后遇到其他符号,可以吧数字和字符完整写入,和其他字符也能正常写入  
  78.             case Character.OTHER_LETTER:  
  79.                 if (length>0) {  
  80.                     bufferIndex--;  
  81.                     offset--;  
  82.                     return flush();  
  83.                 }  
  84.                 push(c);  
  85.                 return flush();  
  86.   
  87.             default:  
  88.                 if (length>0return flush();  
  89.                 break;  
  90.             }  
  91.         }  
  92.     }  
  93.       
  94.     @Override  
  95.     public final void end() {  
  96.       // set final offset  
  97.       final int finalOffset = correctOffset(offset);  
  98.       this.offsetAtt.setOffset(finalOffset, finalOffset);  
  99.     }  
  100.   
  101.     @Override  
  102.     public void reset() throws IOException {  
  103.       super.reset();  
  104.       offset = bufferIndex = dataLen = 0;  
  105.     }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值