Lucene分析器的基类为Analyzer,Analyzer包含两个核心组件:Tokenizer和 TokenFilter。自定义分析器必须实现Analyzer类的抽象方法createComponents(String)来定义TokenStreamComponents。在调用方法tokenStream(String, Reader)的时候,TokenStreamComponents会被重复使用。

自定义分析器首先需要继承Analyzer类,代码如下:

public class HAnalyzer extends Analyzer {

    /*
* 默认不使用停用单词
* */
private boolean useStopWords; private CharArraySet stopWords; public HAnalyzer() {
useStopWords = false;
} public HAnalyzer(CharArraySet stopWords) {
useStopWords = true;
this.stopWords = stopWords;
} @Override
protected TokenStreamComponents createComponents(String fieldName) {
LetterTokenizer tokenizer = new LetterTokenizer();
if(useStopWords) {
return new TokenStreamComponents(tokenizer , new HStopTokenFilter(tokenizer, stopWords));
}
return new TokenStreamComponents(tokenizer);
} }

Analyzer两个核心组件:Tokenizer和 TokenFilter,实现如下:

/*
* 分词解析器,需要定义Token属性CharTermAttribute offsetAttribute
* */
public class LetterTokenizer extends Tokenizer { /*
* 词元文本属性
* */
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); /*
* 词元位移属性
* */
private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); /*
* Token文本最大长度
* */
private static final int MAX_WORD_LEN = 255; /*
* Buffer Size
* */
private static final int IO_BUFFER_SIZE = 4096; private char[] ioBuffer = new char[IO_BUFFER_SIZE]; /*
* Token分隔符集合
* */
private char[] splitChars = {' ',',','.','!'}; /*
* 当前字符串在原字符串中的位置
* */
private int offset = 0; /*
* 当前字符在这一次读取的字符串中的位置
* */
private int bufferIndex = 0; /*
* 每次读取字符串的长度
* */
private int dataLen = 0; @Override
public boolean incrementToken() throws IOException {
clearAttributes(); // 清除前一个Token的所有属性
int length = 0; // 单词的长度
int start = bufferIndex;
char []buffer = termAtt.buffer();
while(true) {
if(bufferIndex >= dataLen) { // 分词处理到ioBuffer末尾时,继续从input读取数据
offset += dataLen;
dataLen = input.read(ioBuffer);
if(dataLen == -1) { // 在Reader读取结束
dataLen = 0;
if(length > 0) { // 虽然从input读取完数据,ioBuffer处理的字符 还没有生成Token
break;
} else {
return false;
}
}
bufferIndex = 0; // 指向ioBuffer的起始位置
}
/**处理ioBuffer读取的字符*/
final char ch = ioBuffer[bufferIndex++];
if(isTokenChar(ch)) { // ch分隔符,形成Token,跳出循环
if(length == 0) {
start = offset + bufferIndex - 1;
} else if(length == buffer.length) {
buffer = termAtt.resizeBuffer(length + 1);
}
if(length == MAX_WORD_LEN) {
break;
}
break;
} else {
buffer[length++] = normalize(ch); // CharTermAttribute文本赋值
}
}
termAtt.setLength(length);
offsetAtt.setOffset(correctOffset(start), correctOffset(start + length));
return true;
} /*
* 规整化--->转为小写
* */
protected char normalize(char ch) {
return Character.toLowerCase(ch);
} /*
* 如果字符ch是分隔符,返回true
* */
protected boolean isTokenChar(char ch) {
for(char c : splitChars) {
if(ch == c) {
return true;
}
}
return false;
} }
/*
* 过滤TokenStream,需要更改Token的PositionIncrementAttribute属性
* */
public class HStopTokenFilter extends TokenFilter { /*
* TokenStream流Token文本属性
* */
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); /*
* 当前Token与前一个Token位移差属性
* */
private PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class); private int skippedPositions; /*
* 停用单词集合
* */
private CharArraySet stopWords; protected HStopTokenFilter(TokenStream input) {
super(input);
} public HStopTokenFilter(TokenStream input , CharArraySet stopWords) {
this(input);
this.stopWords = stopWords;
} @Override
public boolean incrementToken() throws IOException {
clearAttributes(); // 清除上个Token所有属性
skippedPositions = 0;
while(input.incrementToken()) {
if(filter()) { // 过滤掉当前Token,修改skippedPositions
skippedPositions += posIncrAtt.getPositionIncrement();
} else { // 当前Token不可过滤,如果前一个Token被过滤,需修改当前Token的PositionIncrementAttribute属性
if(skippedPositions != 0) {
posIncrAtt.setPositionIncrement(posIncrAtt.getPositionIncrement() + skippedPositions);
}
return true;
}
}
return false;
} private boolean filter() {
return stopWords.contains(termAtt.buffer() , 0 , termAtt.length());
}
}

通过自定义的HAnalyzer,可以完成文本分析,示例如下:

public class Main {

    public static void main(String []args) {
HAnalyzer analyzer = new HAnalyzer();
TokenStream ts = null;
try {
ts = analyzer.tokenStream("myfield", new StringReader("I am a student.My name is Tom!"));
//获取词元位置属性
OffsetAttribute offset = ts.addAttribute(OffsetAttribute.class);
//获取词元文本属性
CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
//重置TokenStream(重置StringReader)
ts.reset();
//迭代获取分词结果
while (ts.incrementToken()) {
System.out.println(offset.startOffset() + " - " + offset.endOffset() + " : " + term.toString() );
}
//关闭TokenStream(关闭StringReader)
ts.end();
} catch (IOException e) {
e.printStackTrace();
}
} }

Lucene分词器的更多相关文章

  1. Lucene学习-深入Lucene分词器,TokenStream获取分词详细信息

    Lucene学习-深入Lucene分词器,TokenStream获取分词详细信息 在此回复牛妞的关于程序中分词器的问题,其实可以直接很简单的在词库中配置就好了,Lucene中分词的所有信息我们都可以从 ...

  2. Lucene系列三:Lucene分词器详解、实现自己的一个分词器

    一.Lucene分词器详解 1. Lucene-分词器API (1)org.apache.lucene.analysi.Analyzer 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分 ...

  3. lucene分词器与搜索

    一.分词器 lucene针对不同的语言和虚伪提供了许多分词器,我们可以针对应用的不同的需求使用不同的分词器进行分词.我们需要注意的是在创建索引时使用的分词器与搜索时使用的分词器要保持一致.否则搜索的结 ...

  4. 学习笔记(三)--Lucene分词器详解

    Lucene-分词器API org.apache.lucene.analysi.Analyzer 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分词处理的TokenStream(分词处理 ...

  5. lucene 分词器

    分词器 作用:切分关键词的. 在什么地方使用到了:在建立索引和搜索时. 原文:An IndexWriter creates and maintains an index. 1,切分: An Index ...

  6. lucene分词器中的Analyzer,TokenStream, Tokenizer, TokenFilter

    分词器的核心类: Analyzer:分词器 TokenStream: 分词器做优点理之后得到的一个流.这个流中存储了分词的各种信息,能够通过TokenStream有效的获取到分词单元. 下面是把文件流 ...

  7. Lucene.net(4.8.0)+PanGu分词器问题记录一:分词器Analyzer的构造和内部成员ReuseStategy

    前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...

  8. Lucene 03 - 什么是分词器 + 使用IK中文分词器

    目录 1 分词器概述 1.1 分词器简介 1.2 分词器的使用 1.3 中文分词器 1.3.1 中文分词器简介 1.3.2 Lucene提供的中文分词器 1.3.3 第三方中文分词器 2 IK分词器的 ...

  9. Lucene.net(4.8.0) 学习问题记录一:分词器Analyzer的构造和内部成员ReuseStategy

    前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...

随机推荐

  1. linux常用命令 查看文件

    Linux常用命令 查看文件 cat命令 cat命令的用途是连接文件或标准打印输入并打印.这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示. 命令格式: cat [ ...

  2. WN7下安装office2013编辑文档反应这么慢?

    把office在高级选项里面“禁用硬件加速”给打勾就OK了. [office 2013密钥] 9MBNG-4VQ2Q-WQ2VF-X9MV2-MPXKV F2V7V-8WN76-77BPV-MKY36 ...

  3. 可折叠的listview 之ExpandableListView基本使用

    先看效果 demo实现 其他的方法和ListView的方法一样,下面来看看具体demo的实现 首先布局文件很简单,就一个控件为: <?xml version="1.0" en ...

  4. 使用vscode开发调试.net core应用程序并部署到Linux跨平台

    使用VS Code开发 调试.NET Core RC2应用程序,由于.NET Core 目前还处于预览版. 本文使用微软提供的示例进行开发及调试. https://github.com/aspnet/ ...

  5. [scrapy] spider object has no attribute '_rules'

    这是因为__init__方法没有继承父类 解决办法: # -*- coding:utf-8 -*- from selenium import webdriver from scrapy.contrib ...

  6. HDU 2551 竹青遍野(循环,水)

    /* 他开始在他的院子种竹子,第1个月种1根竹子,第2个月种8根竹子,第3个月种27根竹子 ...第N个月就种(N^3)根竹子.当他种下第X根竹子那一刻,就是他重出江湖之时! 告诉你X的值, 你能算出 ...

  7. 二维树状数组+差分【p4514】上帝造题的七分钟

    Description "第一分钟,X说,要有矩阵,于是便有了一个里面写满了\(0\)的\(n\times m\)矩阵. 第二分钟,L说,要能修改,于是便有了将左上角为\((a,b)\),右 ...

  8. 17、Flask实战第17天:Flask-cookie

    cookie的基本概念 在网站中,http请求是无状态的.也就是说即使第一次和服务器连接并且登录成功后,第二次请求服务器依然不能知道当前请求的是哪个用户. cookie的出现就是为了解决这个问题,第一 ...

  9. [PKUSC2018]最大前缀和(DP)

    题意:求一个序列随机打乱后最大前缀和的期望. 考场上发现不管怎么设状态都写不出来,实际上只要稍微转换一下就好了. 一个前缀[1..k]是最大前缀,当且仅当前面的所有后缀[k-1,k],[k-2,k], ...

  10. [BZOJ4897][THUSC2016]成绩单(DP)

    4897: [Thu Summer Camp2016]成绩单 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 220  Solved: 132[Subm ...