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. 如何生成pyc/pyo/pyd文件

    # 一.如何生成pyc/pyo文件 # 1.通过编写代码生成 import py_compile # 参数如下 ''' def compile(file, cfile=None, dfile=None ...

  2. javascript字符串中包含特殊字符问题

    我们都知道,在javascript中,字符串写在单引号或者双引号之中.因为这种要求,我们有些时候一些需要的字符串不能够被javascript解析,如下: "We are "Huma ...

  3. AC日记——[JSOI2007]建筑抢修 bzoj 1029

    1029 思路: 贪心,而且,stl水过: 然而神特么输出que.size()就错! 代码: #include <queue> #include <cstdio> #inclu ...

  4. UESTC 1599.wtmsb-STL(priority_queue)

    wtmsb Time Limit: 1000/100MS (Java/Others) Memory Limit: 131072/131072KB (Java/Others) 这天,AutSky_Jad ...

  5. SecureCRT 的上传和下载操作

    在网上找了两篇文章,分别关于ftp和ssh的上传下载,如果有好的大家可以留言分享,不胜感谢~ 因为关于ftp的比较少,就copy上面,本人并没有验证.关于ssh用sr和sz发现一条错误,而且网上也有解 ...

  6. Cocos2d-Lua 做一个活动转盘

    这类活动你肯定见过 关于转盘类型的活动我相信大家多多少少都接触到了,很多的抽奖界面都是这类型的,今天这篇小文章就简单的总结一下我们游戏中需要实现这样一个效果的时候我们该怎样去做,其实只要是Cocos类 ...

  7. Scala 实现快速排序和归并排序

    def quickSort1(array: Array[Int]): Array[Int] = { def swap(x: Int, y: Int): Unit = { val tmp = array ...

  8. 模板-网络流-Dinic

    //Dinic struct Edge{ int from,to,cap,flow; Edge(){ } Edge(int a,int b,int c,int d){ from=a; to=b; ca ...

  9. noi题库 1.7 字符串 10到第15题

    10:简单密码 描述 Julius Caesar曾经使用过一种很简单的密码.对于明文中的每个字符,将它用它字母表中后5位对应的字符来代替,这样就得到了密文.比如字符A用F来代替.如下是密文和明文中字符 ...

  10. 对Array.prototype.slice.call()方法的理解在看别人代码时,发现有这么个写法:[].slice.call(arguments, 0),这到底是什么意思呢?

    1.基础 1)slice() 方法可从已有的数组中返回选定的元素. start:必需.规定从何处开始选取.如果是负数,那么它规定从数组尾部开始算起的位置.也就是说,-1 指最后一个元素,-2 指倒数第 ...