【Lucene】分词器详解,常用的分词器,IKANalyzer

  • 1. 分词器详解
    • 1.1 分词器的作用
    • 1.2 分词器API
      • 1.2.1 示例
      • 1.2.2 Analyzer
      • 1.2.3 createComponents(String fieldName)
      • 1.2.4 TokenStreamComponents
      • 1.2.5 TokenStream
      • 1.2.6 TokenStream 的两类子类
      • 1.2.7 TokenStream 继承了 AttributeSource
      • 1.2.8 AttributeSource使用规则说明
      • 1.2.9 TokenStream 的使用步骤。
  • 2. 常用的分词器
  • 3. IKAnalyzer

1. 分词器详解

1.1 分词器的作用

  • 在创建索引的时候需要用到分词器,在使用字符串搜索的时候也会用到分词器,并且这两个地方要使用同一个分词器,否则可能会搜索不出来结果。

  • 分词器(Analyzer)的作用是把一段文本中的词按规则取出所包含的所有词,对应的是Analyzer类,这是一个抽象类(public abstract class org.apache.lucene.analysis.Analyzer),切分词的具体规则是由子类实现的,所以对于不同的语言规则,要有不同的分词器。

下面我们看看哪里使用了分词器。

    public void createIndex() throws IOException {//1.创建一个Directory对象,指定索引库保存的位置。// 把索引库保存在内存中//Directory directory = new RAMDirectory();// 把索引库保存在磁盘Directory directory = FSDirectory.open(new File("E:\\practice\\lucene\\directory").toPath());//2.基于Directory对象创建一个IndexWriter对象IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());//3.读取磁盘上的文件,对应每个文件创建一个文档对象。File dir = new File("C:\\Users\\siyi\\Desktop\\searchsource");File[] files = dir.listFiles();for(File file : files){//取文件名String fileName = file.getName();//文件的路径String filePath = file.getPath();//文件的内容String fileContent = FileUtils.readFileToString(file,"utf-8");//文件的大小long fileSize = FileUtils.sizeOf(file);//4.向文档对象中添加域//创建Field//参数1:域的名称 参数2:域的内容 参数3:是否存储Field fieldName = new TextField("name", fileName, Field.Store.YES);Field fieldPath = new StoredField("path", filePath);Field fieldContent = new TextField("content",fileContent,Field.Store.YES);Field fieldSizeValue = new LongPoint("size",fileSize);Field fieldSizeStore = new StoredField("size",fileSize);//创建文档对象Document document = new Document();document.add(fieldName);document.add(fieldPath);document.add(fieldContent);document.add(fieldSizeValue);document.add(fieldSizeStore);//5.把文档对象写入索引库indexWriter.addDocument(document);}//6.关闭indexWriter对象indexWriter.close();}

上述程序是一个很普通的构建索引案例。但是我们在程序中并没又发现有分词器的存在。
那是因为我们在创建IndexWriterConfig对象时,默认会使用标准分词器(StandardAnalyzer)。

1.2 分词器API

1.2.1 示例

如果我们想知道分词器分词后的结果,那么我们可以根据下面的程序得到:

    public void testTokenStream() throws Exception {//1.创建一个Analyzer对象,StandardAnalyzer对象Analyzer analyzer = new StandardAnalyzer();//2.使用分析器对象的tokenStream方法获得一个TokenStream对象TokenStream tokenStream = analyzer.tokenStream("", "Lucene 是 apache 软件基金会的一个子项目,由 Doug Cutting 开发,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的库,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene 的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene 是一套用于全文检索和搜寻的开源程式库,由 Apache 软件基金会支持和提供。");//3.向TokenStream对象中设置一个引用,相当于一个指针CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);//4.调用TokenStream对象的rest方法。如果不调用抛异常tokenStream.reset();//5.使用while循环遍历TokenStream对象while(tokenStream.incrementToken()){System.out.println(charTermAttribute.toString());}//6.关闭TokenStream对象tokenStream.close();}

运行结果如下:

我们会发现标准分词器对英文的支持是相当好的。但是对中文却并不支持,标准分词器(StandardAnalyzer)会将每个字都分成一个词,但是我们检索的过程中并不会一个字一个字的查。所以我们需要其他的分词器。

但是在使用其他分词器前我们需要先了解分词器相应的API和原理。

1.2.2 Analyzer

分析器,分词器组件的核心API,它的职责:构建真正对文本进行分词处理的TokenStream(分词处理器)。通过调用它的如下两个方法(这两个方法是final方法,不能被覆盖的。),得到输入文本的分词处理器。

  public final TokenStream tokenStream(final String fieldName,final Reader reader) {TokenStreamComponents components = reuseStrategy.getReusableComponents(this, fieldName);final Reader r = initReader(fieldName, reader);if (components == null) {components = createComponents(fieldName);reuseStrategy.setReusableComponents(this, fieldName, components);}components.setReader(r);return components.getTokenStream();}public final TokenStream tokenStream(final String fieldName, final String text) {TokenStreamComponents components = reuseStrategy.getReusableComponents(this, fieldName);@SuppressWarnings("resource") final ReusableStringReader strReader = (components == null || components.reusableStringReader == null) ?new ReusableStringReader() : components.reusableStringReader;strReader.setValue(text);final Reader r = initReader(fieldName, strReader);if (components == null) {components = createComponents(fieldName);reuseStrategy.setReusableComponents(this, fieldName, components);}components.setReader(r);components.reusableStringReader = strReader;return components.getTokenStream();}
  • 问题1:从哪里得到了TokenStream?
    从components.getTokenStream()得到了TokenStream
  • 问题2:方法传入的字符流Reader 给了谁?
    方法传入的字符流Reader 最终给了Tokenizer的inputPending(类型:Reader):initReader(fieldName, reader)-components.setReader®-source.setReader(reader)-this.inputPending = input;
  • 问题3: components是什么?components的获取逻辑是怎样?
    components是分词处理的组件,components的获取逻辑是有就直接拿来用,没有就新建一个,后面都用新建的这一个
  • 问题4:createComponents(fieldName) 方法是个什么方法?
    是创建分词处理组件的方法
  • 问题5:Analyzer能直接创建对象吗?
    Analyzer是一个抽象类,不能直接创建对象
  • 问题6:为什么它要这样设计?
    使用装饰器模式方便扩展
  • 问题7:Analyzer的实现子类有哪些?
  • 问题8:要实现一个自己的Analyzer,必须实现哪个方法?
    必须实现protected abstract TokenStreamComponents createComponents(String fieldName);

1.2.3 createComponents(String fieldName)

  protected abstract TokenStreamComponents createComponents(String fieldName);

是Analizer中唯一的抽象方法,扩展点。通过提供该方法的实现来实现自己的Analyzer。

参数说明:fieldName,如果我们需要为不同的字段创建不同的分词处理器组件,则可根据这个参数来判断。否则,就用不到这个参数。

返回值为 TokenStreamComponents 分词处理器组件。

我们需要在createComponents方法中创建我们想要的分词处理器组件。

1.2.4 TokenStreamComponents

分词处理器组件:这个类中封装有供外部使用的TokenStream分词处理器。提供了对source(源)和sink(供外部使用分词处理器)两个属性的访问方法。

  • 问题1:这个类的构造方法有几个?区别是什么?从中能发现什么?
    两个构造方法:
    public TokenStreamComponents(final Tokenizer source,final TokenStream result) {this.source = source;this.sink = result;}public TokenStreamComponents(final Tokenizer source) {this.source = source;this.sink = source;}

区别是参数不一样,可以发现source和sink有继承关系

  • 问题2:source 和 sink属性分别是什么类型?这两个类型有什么关系?
Tokenizer source
TokenStream sink
Tokenizer是TokenStream的子类
  • 在这个类中没有创建source、sink对象的代码(而是由构造方法传入)。也就是说我们在Analyzer.createComponents方法中创建它的对象前,需先创建source、sink

  • 问题4:在Analyzer中tokenStream() 方法中把输入流给了谁?得到的TokenStream对象是谁?TokenStream对象sink中是否必须封装有source对象?
    输入流给了source.setReader(reader),得到的TokenStream对象是TokenStream sink

components.getTokenStream()public TokenStream getTokenStream() {return sink;
}

1.2.5 TokenStream

分词处理器,负责对输入文本完成分词、处理。

  • 问题1:TokenStream对象sink中是否必须封装有source对象,TokenStream中有没有对应的给入方法?
    没有

  • 问题2:TokenStream是一个抽象类,有哪些方法,它的抽象方法有哪些?它的构造方法有什么特点?
    抽象方法:

    public abstract boolean incrementToken() throws IOException;
    

    构造方法有两个:

     protected TokenStream(AttributeSource input) {super(input);assert assertFinal();}protected TokenStream(AttributeFactory factory) {super(factory);assert assertFinal();}
    

    概念说明:Token: 分项,从字符流中分出一个一个的项

  • 问题3:TokenStream的具体子类分为哪两类?有什么区别?
    Tokenizer:分词器,输入是Reader字符流的TokenStream,完成从流中分出分项
    TokenFilter:分项过滤器,它的输入是另一个TokenStream,完成对从上一个TokenStream中流出的token的特殊处理。

  • 问题4:TokenStream继承了谁?它是干什么用的?
    继承了AttributeSource,TokenStream进行分词处理的
    概念说明:Token Attribute: 分项属性(分项的信息):如 包含的词、位置等

1.2.6 TokenStream 的两类子类

Tokenizer:分词器,输入是Reader字符流的TokenStream,完成从流中分出分项

TokenFilter:分项过滤器,它的输入是另一个TokenStream,完成对从上一个TokenStream中流出的token的特殊处理。

  • 问题1:这个类该如何使用?要实现自己的Tokenizer只需要做什么?
    要实现自己的Tokenizer只需要继承Tokenizer复写incrementToken()方法

  • 问题2:请查看TokenFilter类的源码及注释,如何实现自己的TokenFilter?
    要实现自己的TokenFilter只需要继承TokenFilter复写incrementToken()方法

  • 问题3:TokenFilter的子类有哪些?

  • 问题4:TokenFilter是不是一个典型的装饰器模式?如果我们需要对分词进行各种处理,只需要按我们的处理顺序一层层包裹即可(每一层完成特定的处理)。不同的处理需要,只需不同的包裹顺序、层数。

1.2.7 TokenStream 继承了 AttributeSource

  • 问题1:我们在TokenStream及它的两个子类中是否有看到关于分项信息的存储,如该分项的词是什么、这个词的位置索引?

概念说明:Attribute 属性 Token Attribute 分项属性(分项信息),如 分项的词、词的索引位置等等。这些属性通过不同的Tokenizer /TokenFilter处理统计得出。不同的Tokenizer/TokenFilter组合,就会有不同的分项信息。它是会动态变化的,你不知道有多少,是什么。那该如何实现分项信息的存储呢?

答案就是 AttributeSource、Attribute 、AttributeImpl、AttributeFactory

1、AttribureSource 负责存放Attribute对象,它提供对应的存、取方法

2、Attribute对象中则可以存储一个或多个属性信息

3、AttributeFactory 则是负责创建Attributre对象的工厂,在TokenStream中默认使用了AttributeFactory.getStaticImplementation 我们不需要提供,遵守它的规则即可。

1.2.8 AttributeSource使用规则说明

  • 某个TokenStream实现中如要存储分项属性,通过AttributeSource的两个add方法之一,往AttributeSource中加入属性对象。

<T extends Attribute> T addAttribute(Class<T> attClass) 该方法要求传人你需要添加的属性的接口类(继承Attribute),返回对应的实现类实例给你。从接口到实例,这就是为什么需要AttributeFactory的原因。这个方法是我们常用的方法

void addAttributeImpl(AttributeImpl att)
  • 加入的每一个Attribute实现类在AttributeSource中只会有一个实例,分词过程中,分项是重复使用这一实例来存放分项的属性信息。重复调用add方法添加它返回已存储的实例对象。

  • 要获取分项的某属性信息,则需持有某属性的实例对象,通过addAttribute方法或getAttribure方法获得Attribute对象,再调用实例的方法来获取、设置值

  • 在TokenStream中,我们用自己实现的Attribute,默认的工厂。当我们调用这个add方法时,它怎么知道实现类是哪个?这里有一定规则要遵守:

    1、自定义的属性接口 MyAttribute 继承 Attribute

    2、自定义的属性实现类必须继承 Attribute,实现自定义的接口MyAttribute

    3、自定义的属性实现类必须提供无参构造方法

    4、为了让默认工厂能根据自定义接口找到实现类,实现类名需为:接口名+Impl 。

1.2.9 TokenStream 的使用步骤。

我们在应用中并不直接使用分词器,只需为索引引擎和搜索引擎创建我们想要的分词器对象。但我们在选择分词器时,会需要测试分词器的效果,就需要知道如何使用得到的分词处理器TokenStream,使用步骤:

1、从tokenStream获得你想要获得分项属性对象(信息是存放在属性对象中的)

2、调用 tokenStream 的 reset() 方法,进行重置。因为tokenStream是重复利用的。

3、循环调用tokenStream的incrementToken(),一个一个分词,直到它返回false

4、在循环中取出每个分项你想要的属性值。

5、调用tokenStream的end(),执行任务需要的结束处理。

6、调用tokenStream的close()方法,释放占有的资源。

2. 常用的分词器

分词和查询都是以词项为基本单位,词项是词条化的结果。在Lucene中分词主要依靠Analyzer类解析实现。Analyzer类是一个抽象类,分词的具体规则是由子类实现的,所以对于不同的语言规则,要有不同的分词器

  1. StopAnalyzer
    停用词分词器:能过滤词汇中的特定字符串和词汇,并且完成大写转小写的功能。

  2. StandardAnalyzer
    标准分词器:根据空格和符号来完成分词,还可以完成数字、字母、E-mail地址、IP地址以及中文字符的分析处理,还可以支持过滤词表,用来代替StopAnalyzer能够实现的过滤功能。

  3. WhitespaceAnalyzer
    空格分词器:使用空格作为间隔符的词汇分割分词器。处理词汇单元的时候,以空格字符作为分割符号。分词器不做词汇过滤,也不进行小写字符转换。实际中可以用来支持特定环境下的西文符号的处理。由于不完成单词过滤和小写字符转换功能,也不需要过滤词库支持。词汇分割策略上简单使用非英文字符作为分割符,不需要分词词库支持。

  4. SimleAnalyzer
    简单分词:具备基本西文字符词汇分析的分词器,处理词汇单元时,以非字母字符作为分割符号。分词器不能做词汇的过滤,之进行词汇的分析和分割。输出地词汇单元完成小写字符转换,去掉标点符号等分割符。

  5. CJKAnalyzer
    二分法分词:内部调用CJKAnalyzer分词器,对中文进行分词,同时使用StopFilt过滤器完成过滤功能,可以实现中文的多元切分和停用词过滤。

  6. IKAnalyzer
    IKAnalyzer实现了以词典为基础的正反向全切分,以及正反向最大匹配切分两种方法。IKAnalyzer是第三方实现的分词器,继承自Lucene的Analyzer类,针对中文文本进行处理。

  7. Paoding Analysis
    Paoding Analysis中文分词具有极 高效率 和 高扩展性。引入隐喻,采用完全的面向对象设计,构思先进。其效率比较高,在PIII 1G内存个人机器上,1秒可准确分词100万汉字。采用基于不限制个数的词典文件对文章进行有效切分,使能够将对词汇分类定义。能够对未知的词汇进行合理解析。

  8. MMSeg4J
    mmseg4j 用 Chih-Hao Tsai 的 MMSeg 算法实现的中文分词器(http://technology.chtsai.org/mmseg/ ),并实现 lucene 的 analyzer 和 solr 的TokenizerFactory 以方便在Lucene和Solr中使用。 MMSeg 算法有两种分词方法:Simple和Complex,都是基于正向最大匹配。Complex 加了四个规则过虑。官方说:词语的正确识别率达到了 98.41%。mmseg4j 已经实现了这两种分词算法。

3. IKAnalyzer

因为我这儿指的IKAnalyzer是国内某大佬开发的,所以找不到依赖坐标。

  • IKAnalyzer相关文件下载:
    链接: https://pan.baidu.com/s/1jyhKVG0NQoW3OdQBV92_kg 提取码: 3bjq

  • 使用方法:
    第一步:把jar包添加到工程中
    第二步:把配置文件和扩展词典和停用词词典添加到classpath下

  • 注意:hotword.dic和ext_stopword.dic文件的格式为UTF-8,注意是无BOM 的UTF-8 编码。
    也就是说禁止使用windows记事本编辑扩展词典文件

    public void testTokenStream2() throws Exception {//1.创建一个Analyzer对象,StandardAnalyzer对象Analyzer analyzer = new IKAnalyzer();//2.使用分析器对象的tokenStream方法获得一个TokenStream对象TokenStream tokenStream = analyzer.tokenStream("", "Linux 刚面世时并没有图形界面,所有的操作全靠命令完成,如 磁盘操作、文件存取、目录操作、进程管理、文件权限 设定等");//3.向TokenStream对象中设置一个引用,相当于一个指针CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);//4.调用TokenStream对象的rest方法。如果不调用抛异常tokenStream.reset();//5.使用while循环遍历TokenStream对象while(tokenStream.incrementToken()){System.out.println(charTermAttribute.toString());}//6.关闭TokenStream对象tokenStream.close();}

运行结果:

【Lucene】分词器详解,常用的分词器,IKANalyzer相关推荐

  1. python装饰器详解-Python 函数装饰器

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

  2. python类装饰器详解-Python类装饰器实现方法详解

    本文实例讲述了Python类装饰器.分享给大家供大家参考,具体如下: 编写类装饰器 类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例. 单体类 ...

  3. python类装饰器详解-Python 类装饰器解析

    1. 类装饰器(都不带参数) class ClassDeco: def __init__(self, func): self.func = func def __call__(self, *args, ...

  4. C++析构器详解【C++析构器】

    析构器(Destructor) 手动释放到自动销毁 发生时机 析构器的作用 语法规则 析构规则 析构器小结 注意 析构器(Destructor) 手动释放到自动销毁 我们通过传入参数的方式,动态的申请 ...

  5. Paint画笔的常用api简介、渲染器详解

    文章目录 常用api 1.setStrokeCap圆角效果 2.setStrokeJoin拐角风格 3.setFilterBitmap设置双线性过滤 4.getFontMetrics获取字体度量对象 ...

  6. OpenGL ES _ 着色器_片断着色器详解

    OpenGL ES _ 入门_01 OpenGL ES _ 入门_02 OpenGL ES _ 入门_03 OpenGL ES _ 入门_04 OpenGL ES _ 入门_05 OpenGL ES ...

  7. JVM------类加载器详解

    JVM------类加载器详解 1.图解类加载器工作流程 2.类加载器种类 3.类加载器的加载顺序 4.一些需要了解的机制 1.图解类加载器工作流程 2.类加载器种类 启动类加载器(Bootstrap ...

  8. ansj中文分词器详解

    ansj中文分词器详解   最近在做一个人工智能的项目,其中用到了分词功能.从网上找了一些java用于分词的工具,最终选择了ansj中文分词器.个人认为效果和功能是比较优秀的.本文将对ansj的所有功 ...

  9. zend framework php编码规范,Zend Framework常用校验器详解

    本文实例讲述了Zend Framework常用校验器.分享给大家供大家参考,具体如下: Date日期校验器 代码:<?php require_once 'Zend/Validate/Date.p ...

最新文章

  1. 一个二维码能装10T电影?看样子我也能改变世界了
  2. Jquery Ajax调用aspx页面方法
  3. 使用Oracle数据泵迁移数据库
  4. 数据库:B+树索引和Hash索引得区别
  5. 火箭轨道计算属于什么计算机技术,2018年计算机二级高级Office每日一练 2月27日...
  6. python 修饰器_python中用修饰器进行异常日志记录
  7. android 监听安装来源_Android编程监听APK安装与删除等过程的方法
  8. Qt4_Quit按键
  9. Tomcat9 安装与配置
  10. C语言图书管理信息系统
  11. iTunes C# Mobile Device API代码-立哥开发
  12. 斯蒂芬•盖斯 — 《微习惯》
  13. Linux内核移植常见错误及解决方法
  14. Spark SQL DataFrame新增一列的四种方法
  15. 冯氏结构、哈佛结构、超级哈佛结构之间的异同
  16. 软件企业必备的认证资质证书
  17. informatica 学习日记整理
  18. 北师大计算机试题六答案,2019年度全国计算机二级考试-试题资料题库(附答案解析).doc...
  19. 恒生电子Java工程师_【恒生电子工资】java开发工程师待遇-看准网
  20. MIPI 系列之 DPI

热门文章

  1. 网上坏蜘蛛搜索引擎bot/spider等HTTP USER AGENT关键字一览
  2. 电能管理系统运用互联网和大数据技术,为电力运维公司和售配电公司提供变电所运维云平
  3. mysql多表同步es一个索引_使用 Logstash 同步多个数据表到 Elasticsearch 同一个索引 (Index)...
  4. 计算机专业选i5八代还是i7八代,七代酷睿i7与第八代酷睿i5哪个好
  5. ## 解决iPhone“抹掉所有内容和设置”时,弹出“验证失败”的问题?解决办法如下:
  6. 精彩!京东T7开创“新算法宝典”,图文并茂,全新演绎,太酷了
  7. asp.net快递物流信息网
  8. Money20/20中国大会亮点嘉宾及议程公布
  9. 当今职场上广为流传的十大精辟语录
  10. 阿里云服务器ECS搭建网站完整方法教程