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. Spring Boot 项目部署到本地Tomcat,出现访问路径问题

    首先确定war 包没问题,把war包放在webapps目录下,访问http://localhost:8080/ + 项目名称 发现首页可以访问但css,js请求都是404,跳转页面也是404 解决方法 ...

  2. selenium TestNG 依赖和忽略测试

    依赖:通过使用Test 注释的dependsOnMethods={"verifyLogin"}子句,verifyAccountInfo 测试指定了它依赖verifyLogin()方 ...

  3. hdu 5186(模拟)

    zhx's submissions Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...

  4. Spring Mvc中@ResponseBody中文乱码解决,以及修改返回的Content-Type

    http://www.codeif.com/topic/784 spring 3 mvc 的 @ResponseBody返回数据用起来很方便,但是中文乱码,而且返回的Content-Type不带编码信 ...

  5. linux中Firefox浏览器 手动安装 flash

    打开firefox浏览器,当你打开有关音频或者视频的网站时候,会提示你安装 flash,可是,官网提示,需要手动安装. 1.先从提示的官网上下载好文件 “install_flash_player_11 ...

  6. CF 1005B Delete from the Left 【模拟数组操作/正难则反】

    You are given two strings s and t. In a single move, you can choose any of two strings and delete th ...

  7. HDU 多校1.11

  8. HDU 2547 无剑无我(数学)

    #include<cstdio> #include<iostream> #include<cmath> int main() { double a,b,c,d,m; ...

  9. Android学习--跨程序共享数据之内容提供其探究

    什么是内容提供器? 跨程序共享数据之内容提供器,这是个什么功能?看到这个名称的时候最能给我们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的作用主要是用于在不同的引用程序之间实 ...

  10. springboot Hello World探究

    Hello World探究1.POM文件1.父项目 <parent>     <groupId>org.springframework.boot</groupId> ...