`
weitao1026
  • 浏览: 1053143 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Lucene的内置的分词器

阅读更多
本来的Lucene的内置的分词器,差不多可以完成我们的大部分分词工作了,如果是英文文章那么可以使用StandardAnalyzer标准分词器,WhitespaceAnalyzer空格分词器,对于中文我们则可以选择IK分词器,Messeg4j,庖丁等分词器。

我们先来看看下面的几个需求


编号 需求分析
1 按单个字符进行分词无论是数字,字母还是特殊符号
2 按特定的字符进行分词,类似String中spilt()方法
3 按照某个字符或字符串进行分词

仔细分析下上面的需求,会觉得上面的需求很没意思,但是在特定的场合下确实是存在这样的需求的,看起来上面的需求很简单,但是lucene里面内置的分析器却没有一个支持这种变态的"无聊的"分词需求,如果想要满足上面的需求,可能就需要我们自己定制自己的分词器了。


先来看第一个需求,单个字符切分,这就要不管你是the一个单词还是一个电话号码还是一段话还是其他各种特殊符号都要保留下来,进行单字切分,这种特细粒度的分词,有两种需求情况,可能适应这两种场景
(-)100%的实现数据库模糊匹配
(=)对于某个电商网站笔记本的型号Y490,要求用户无论输入Y还是4,9,0都可以找到这款笔记本


这种单字切分确实可以实现数据库的百分百模糊检索,但是同时也带来了一些问题,如果这个域中是存电话号码,或者身份证之类的与数字的相关的信息,那么这种分词法,会造成这个域的倒排链表非常之长,反映到搜索上,就会出现中文检索很快,而数字的检索确实非常之慢的问题。原因是因为数字只有0-9个字符,而汉字则远远比这个数量要大的多,所以在选用这种分词时,还是要慎重的考虑下自己的业务场景到底适不适合这种分词,否则就会可能出一些问题。

再来分析下2和3的需求,这种需求可能存在这么一种情况,就是某个字段里存的内容是按照逗号或者空格,#号,或者是自己定义的一个字符串进行分割存储的,而这种时候我们可能就会想到一些非常简单的做法,直接调用String类的spilt方法进行打散,确实,这种方式是可行的,但是lucene里面的结构某些情况下,就可能不适合用字符串拆分的方法,而是要求我们必须定义一个自己的分词器来完成这种功能,因为涉及到一些参数需要传一个分词器或者索引和检索时都要使用分词器来构造解析,所以有时候就必须得自己定义个专门处理这种情况的分词器了。

好了,散仙不在唠叨了,下面开始给出代码,首先针对第一个需求,单字切分,其实这个需求没什么难的,只要熟悉lucene的Tokenizer就可以轻松解决,我们改写ChineseTokenizer来满足我们的需求.



Java代码 复制代码 收藏代码
1.package com.piaoxuexianjing.cn; 
2. 
3.import java.io.IOException; 
4.import java.io.Reader; 
5. 
6.import org.apache.lucene.analysis.Tokenizer; 
7.import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 
8.import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; 
9.import org.apache.lucene.util.AttributeSource.AttributeFactory; 
10. 
11.public class China extends Tokenizer { 
12.     
13.     public China(Reader in) { 
14.          super(in); 
15.        } 
16. 
17.        public China(AttributeFactory factory, Reader in) { 
18.          super(factory, in); 
19.        } 
20.            
21.        private int offset = 0, bufferIndex=0, dataLen=0; 
22.        private final static int MAX_WORD_LEN = 255; 
23.        private final static int IO_BUFFER_SIZE = 1024; 
24.        private final char[] buffer = new char[MAX_WORD_LEN]; 
25.        private final char[] ioBuffer = new char[IO_BUFFER_SIZE]; 
26. 
27. 
28.        private int length; 
29.        private int start; 
30. 
31.        private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); 
32.        private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); 
33.         
34.        private final void push(char c) { 
35. 
36.            if (length == 0) start = offset-1;            // start of token 
37.            buffer[length++] = Character.toLowerCase(c);  // buffer it 
38. 
39.        } 
40. 
41.        private final boolean flush() { 
42. 
43.            if (length>0) { 
44.                //System.out.println(new String(buffer, 0, 
45.                //length)); 
46.              termAtt.copyBuffer(buffer, 0, length); 
47.              offsetAtt.setOffset(correctOffset(start), correctOffset(start+length)); 
48.              return true; 
49.            } 
50.            else 
51.                return false; 
52.        } 
53. 
54.        @Override 
55.        public boolean incrementToken() throws IOException { 
56.            clearAttributes(); 
57. 
58.            length = 0; 
59.            start = offset; 
60. 
61. 
62.            while (true) { 
63. 
64.                final char c; 
65.                offset++; 
66. 
67.                if (bufferIndex >= dataLen) { 
68.                    dataLen = input.read(ioBuffer); 
69.                    bufferIndex = 0; 
70.                } 
71. 
72.                if (dataLen == -1) { 
73.                  offset--; 
74.                  return flush(); 
75.                } else 
76.                    c = ioBuffer[bufferIndex++]; 
77. 
78. 
79.                switch(Character.getType(c)) { 
80. 
81.                case Character.DECIMAL_DIGIT_NUMBER://注意此部分不过滤一些熟悉或者字母 
82.                case Character.LOWERCASE_LETTER://注意此部分 
83.                case Character.UPPERCASE_LETTER://注意此部分 
84.//                  push(c); 
85.//                  if (length == MAX_WORD_LEN) return flush(); 
86.//                  break; 
87.              
88.                case Character.OTHER_LETTER: 
89.                    if (length>0) { 
90.                        bufferIndex--; 
91.                        offset--; 
92.                        return flush(); 
93.                    } 
94.                    push(c); 
95.                    return flush(); 
96. 
97.                default: 
98.                    if (length>0) return flush(); 
99.                      
100.                        break; 
101.                     
102.                } 
103.            } 
104.        } 
105.         
106.        @Override 
107.        public final void end() { 
108.          // set final offset 
109.          final int finalOffset = correctOffset(offset); 
110.          this.offsetAtt.setOffset(finalOffset, finalOffset); 
111.        } 
112. 
113.        @Override 
114.        public void reset() throws IOException { 
115.          super.reset(); 
116.          offset = bufferIndex = dataLen = 0; 
117.        } 
118. 
119.} 



然后定义个自己的分词器



Java代码 复制代码 收藏代码
1.package com.piaoxuexianjing.cn; 
2. 
3.import java.io.Reader; 
4. 
5.import org.apache.lucene.analysis.Analyzer; 
6.import org.apache.lucene.analysis.Tokenizer; 
7. 
8./**
9. * @author 三劫散仙
10. * 单字切分
11. * 
12. * **/ 
13.public class MyChineseAnalyzer extends Analyzer { 
14. 
15.    @Override 
16.    protected TokenStreamComponents createComponents(String arg0, Reader arg1) { 
17.        
18.        Tokenizer token=new China(arg1); 
19.         
20.        return new TokenStreamComponents(token); 
21.    } 
22.     
23.     
24.     
25.     
26. 
27.} 


下面我们来看单字切词效果,对于字符串
String text="天气不错132abc@#$+-)(*&^.,/";



Java代码 复制代码 收藏代码
1.天 
2.气 
3.不 
4.错 
5.1 
6.3 
7.2 
8.a 
9.b 
10.c 
11.@ 
12.# 
13.$ 
14.+ 
15.- 
16.) 
17.( 
18.* 
19.& 
20.^ 
21.. 
22., 
23./ 



对于第二种需求我们要模仿空格分词器的的原理,代码如下



Java代码 复制代码 收藏代码
1.package com.splitanalyzer; 
2. 
3.import java.io.Reader; 
4. 
5.import org.apache.lucene.analysis.util.CharTokenizer; 
6.import org.apache.lucene.util.Version; 
7. 
8./***
9. *
10. *@author 三劫散仙
11. *拆分char Tokenizer
12. * 
13. * */ 
14.public class SpiltTokenizer extends CharTokenizer { 
15.  
16.       char c; 
17.    public SpiltTokenizer(Version matchVersion, Reader input,char c) { 
18.        super(matchVersion, input); 
19.        // TODO Auto-generated constructor stub 
20.        this.c=c; 
21.    } 
22. 
23.    @Override 
24.    protected boolean isTokenChar(int arg0) { 
25.        return arg0==c?false:true ; 
26.    } 
27.     
28.     
29.     
30. 
31.} 


然后在定义自己的分词器



Java代码 复制代码 收藏代码
1.package com.splitanalyzer; 
2. 
3.import java.io.Reader; 
4. 
5.import org.apache.lucene.analysis.Analyzer; 
6.import org.apache.lucene.util.Version; 
7. 
8./**
9. * @author 三劫散仙
10. * 自定义单个char字符分词器
11. * **/ 
12.public class SplitAnalyzer extends Analyzer{ 
13.    char c;//按特定符号进行拆分 
14.     
15.    public SplitAnalyzer(char c) { 
16.        this.c=c; 
17.    } 
18. 
19.    @Override 
20.    protected TokenStreamComponents createComponents(String arg0, Reader arg1) { 
21.        // TODO Auto-generated method stub 
22.        return  new TokenStreamComponents(new SpiltTokenizer(Version.LUCENE_43, arg1,c)); 
23.    } 
24.     
25. 
26.} 


下面看一些测试效果



Java代码 复制代码 收藏代码
1.package com.splitanalyzer; 
2. 
3.import java.io.StringReader; 
4. 
5.import org.apache.lucene.analysis.TokenStream; 
6.import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 
7. 
8./**
9. * 测试的demo
10. * 
11. * **/ 
12.public class Test { 
13.     
14.    public static void main(String[] args)throws Exception { 
15.         SplitAnalyzer analyzer=new SplitAnalyzer('#'); 
16.             //SplitAnalyzer analyzer=new SplitAnalyzer('+'); 
17.        //PatternAnalyzer analyzer=new PatternAnalyzer("abc"); 
18.        TokenStream ts= analyzer.tokenStream("field", new StringReader("我#你#他")); 
19.       // TokenStream ts=   analyzer.tokenStream("field", new StringReader("我+你+他")); 
20.        CharTermAttribute term=ts.addAttribute(CharTermAttribute.class); 
21.        ts.reset(); 
22.        while(ts.incrementToken()){ 
23.            System.out.println(term.toString()); 
24.        } 
25.        ts.end(); 
26.        ts.close(); 
27.          
28.    } 
29. 
30.} 
31.我 
32.你 
33.他 



到这里,可能一些朋友已经看不下去了,代码太多太臃肿了,有没有一种通用的办法,解决此类问题,散仙的回答是肯定的,如果某些朋友,连看到这部分的耐心都没有的话,那么,不好意思,你只能看到比较低级的解决办法了,当然能看到这部分的道友们,散仙带着大家来看一下比较通用解决办法,这个原理其实是基于正则表达式的,所以由此看来,正则表达式在处理文本字符串上面有其独特的优势。下面我们要做的就是改写自己的正则解析器,代码非常精简,功能却是很强大的,上面的3个需求都可以解决,只需要传入不用的参数即可。





Java代码 复制代码 收藏代码
1.package com.splitanalyzer; 
2. 
3.import java.io.Reader; 
4.import java.util.regex.Pattern; 
5. 
6.import org.apache.lucene.analysis.Analyzer; 
7.import org.apache.lucene.analysis.pattern.PatternTokenizer; 
8. 
9./**
10. * @author 三劫散仙
11. * 自定义分词器
12. * 针对单字切
13. * 单个符号切分
14. * 多个符号组合切分
15. * 
16. * **/ 
17.public class PatternAnalyzer  extends Analyzer { 
18.     
19.    String regex;//使用的正则拆分式 
20.    public PatternAnalyzer(String regex) { 
21.         this.regex=regex; 
22.    } 
23. 
24.    @Override 
25.    protected TokenStreamComponents createComponents(String arg0, Reader arg1) { 
26.        return new TokenStreamComponents(new PatternTokenizer(arg1, Pattern.compile(regex),-1)); 
27.    } 
28.     
29.     
30.     
31.} 



我们来看下运行效果:



Java代码 复制代码 收藏代码
1.package com.splitanalyzer; 
2. 
3.import java.io.StringReader; 
4. 
5.import org.apache.lucene.analysis.TokenStream; 
6.import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 
7. 
8./**
9. * 测试的demo
10. * 
11. * **/ 
12.public class Test { 
13.     
14.    public static void main(String[] args)throws Exception { 
15.       //  SplitAnalyzer analyzer=new SplitAnalyzer('#'); 
16.         PatternAnalyzer analyzer=new PatternAnalyzer(""); 
17.         //空字符串代表单字切分   
18.        TokenStream ts= analyzer.tokenStream("field", new StringReader("我#你#他")); 
19.        CharTermAttribute term=ts.addAttribute(CharTermAttribute.class); 
20.        ts.reset(); 
21.        while(ts.incrementToken()){ 
22.            System.out.println(term.toString()); 
23.        } 
24.        ts.end(); 
25.        ts.close(); 
26.          
27.    } 
28. 
29.} 


输出效果:



Java代码 复制代码 收藏代码
1.我 
2.# 
3.你 
4.# 
5.他 


传入#号参数



Java代码 复制代码 收藏代码
1.PatternAnalyzer analyzer=new PatternAnalyzer("#"); 


输出效果:



Java代码 复制代码 收藏代码
1.我 
2.你 
3.他 


传入任意长度的字符串参数



Java代码 复制代码 收藏代码
1.PatternAnalyzer analyzer=new PatternAnalyzer("分割"); 
2.okenStream ts=  analyzer.tokenStream("field", new StringReader("我分割你分割他分割")); 


输出效果:



Java代码 复制代码 收藏代码
1.我 
2.你 
3.他 
分享到:
评论

相关推荐

    简单的LUCENE分词

    在JAVA下,用lucene的内置分词功能对XML文件进行分词,并取消无用词

    lucene.net +盘古分词器 实例

    Lucene.Net只是一个全文检索开发包,不是一个成型的搜索引擎 它提供了类似SQLServer数据库正式版中的全文检索功能的... 但是Lucene.Net内置分词算法对中文支持不是很好,以下会使用国内较为流行的分词算法 -- 盘古分词

    lucene中文分词公用组件V1.4

    在早期版本V1.3中,当遇到特定的数量词时,分词过程可能会导致程序出错,这是因为分词器在处理某些边界情况时,对字符串指针的管理存在漏洞。V1.4对此进行了修正,确保了分词过程的稳定性和安全性,避免了因错误导致...

    分词器LUcene

    Lucene通过内置的IK分词器、SmartChinese分词器等,提供了高效且准确的中文分词解决方案。 接下来,我们关注的是分词歧义处理。在中文分词中,常常会出现一词多解的情况,这就是分词歧义。例如,“银行”可以是金融...

    lucene Analyzer 庖丁解牛 中文分词

    词典包含了大量已知的词语,分词器会根据词典来识别出连续的字符序列是否为一个完整的词语。词典的大小和质量直接影响了分词的准确性。Lucene允许用户自定义词典,以适应特定领域的分词需求。 2. **模糊匹配和歧义...

    Lucene关于几种中文分词的总结

    以下是对几种常见Lucene中文分词器的总结: 1. CJKAnalyzer:此分词器是Lucene contrib目录下的analyzers子目录中的组件,位于cn目录。CJKAnalyzer主要针对中日韩三国语言进行分词,它基于字符级别进行分词,适用于...

    ik分词器tar包 7.10.2

    1. **丰富的词库**:IK分词器内置了大量中文词汇,能够对常见词汇进行准确分词。同时,它支持用户自定义词典,允许根据具体业务需求添加或修改词库,提高分词效果。 2. **智能分析模式**:IK分词器提供“smart”和...

    es安装ik分词器

    它提供了多种内置分词器来处理文本数据,但在实际应用过程中,由于中文语言的复杂性,Elasticsearch 默认提供的分词器往往不能很好地满足中文分词的需求。因此,通常需要安装第三方的中文分词器插件来提高中文处理...

    最新版Lucene.Net盘古分词2.0

    2. **分词处理**:通过内置的分析器,`Lucene.Net`可以将输入的文本进行分词,为后续的索引和查询做准备。 3. **查询解析**:支持复杂的查询语法,能将用户输入的查询语句转换为高效的查询计划。 4. **文档存储**:...

    支持lucene的词典机械中文分词

    在Lucene中,分词器通常与索引构建过程相结合,通过预处理和并行化处理来减少在线查询时的负担。 总的来说,支持Lucene的词典机械中文分词方法结合了反向分词策略和特定的数字、英文处理机制,能够有效地处理中文...

    elasticserach 7.17.4版本的中文 IK分词器

    1. **丰富的词库**:IK 分词器内置了大量词汇,涵盖了常见的中文词汇和专有名词,同时也支持用户自定义词典,以满足特定领域的分词需求。 2. **智能分析**:IK 分词器采用了动态词典加载和智能切分算法,可以适应...

    分词器6659282.zip

    在Solr中,有多种内置的分词器可供选择,如StandardTokenizer、SimpleTokenizer、KeywordTokenizer等,每种都有其特定的分词规则。例如,StandardTokenizer遵循Unicode标准,能够处理大多数语言的文本,而...

    中文搜索分词lucene包+paoding包

    在中文处理方面,Lucene虽然内置了一些基本的分词功能,但是对于复杂的中文词汇和短语,其原生的分词效果可能不够理想。这就需要引入专门针对中文的分词工具。 Paoding,又名“庖丁”,是专门为Java平台设计的一个...

    elasticsearch 中文分词器ik

    1. **丰富的词典资源**:IK分词器内置了大量常用词典,涵盖了新闻、网络词汇等多个领域,同时支持用户自定义词典,方便添加专业术语或个性化词汇。 2. **动态扩展性**:IK分词器支持在运行时动态热加载新词典,无需...

    lucene3.6.jar

    在Lucene中,分词器是关键组件之一,因为搜索引擎的工作很大程度上依赖于准确的分词结果。IkAnalyzer对中文的处理能力强大,支持多种分词模式,包括全模式、精确模式、最短路径模式等,以满足不同场景的需求。它还...

    Lucene建立索引jar包和Paoding分词jar包

    它的特点包括支持多种分词模式(精确、全模式、简明模式等),内置丰富的词典,以及高效的分词算法。在Lucene中,我们可以通过集成paoding-analysis.jar来利用Paoding进行中文分词,提升索引质量和检索效果。 四、...

    IKAnalyzer中文分词器

    ### IKAnalyzer中文分词器V3.1.6:深入解析与使用指南 #### 一、IKAnalyzer3.0概述 IKAnalyzer是一个基于Java语言开发的高性能中文分词工具包,自2006年发布以来,已历经多次迭代升级。起初作为开源项目Lucene的一...

    ikanalyzer中文分词支持lucene7.1.0

    1. **丰富的词典**:ikanalyzer内置了大量中文词汇,涵盖了日常用语、专业术语等,能够对中文文本进行准确的分词。 2. **动态加载词典**:用户可以根据需求自定义词典,实现特定领域的分词优化。 3. **智能分析**:...

    solr7.x-ik分词器亲测可用.zip

    "solr7.x-ik分词器亲测可用.zip" 文件是一个包含针对 Solr 7.x 版本优化的 IK 分词器的压缩包,IK(Intelligent Chinese Analyzer)是广泛使用的中文分词库,专为处理中文文本而设计。这个亲测可用的版本意味着已经...

    ik分词器ik-analyzer-5.3.0和ikanalyzer-solr6.5-2018

    标题 "ik分词器ik-analyzer-5.3.0和ikanalyzer-solr6.5-2018" 涉及的是两个不同版本的IK分词器,一个是ik-analyzer-5.3.0,适用于Solr 5.5.0,另一个是ikanalyzer-solr6.5-2018,适用于Solr 7.0.0。IK分词器是Java...

Global site tag (gtag.js) - Google Analytics