Lucene.Net 2.3.1开发介绍 —— 二、分词(二)
原文:Lucene.Net 2.3.1开发介绍 —— 二、分词(二)
1.2、分词的过程
1.2.1、分词器工作的过程
内置的分词器效果都不好,那怎么办?只能自己写了!在写之前当然是要先看看内置的分词器是怎么实现的了。从1.1分析分词效果,可以看出KeywordAnalyzer这个分词器最懒惰,基本什么事情也没做。并不是它不会做,而是我们没找到使用它的方法,就像手上拿着个盒子,不知道里面是什么,就不知道这个是干嘛的,有什么用。打开盒子,那就是要查看源代码了!
代码 1.2.1.1
Code 1using System; 2 3namespace Lucene.Net.Analysis 4{ 5 6 /**//// <summary> "Tokenizes" the entire stream as a single token. This is useful 7 /// for data like zip codes, ids, and some product names. 8 /// </summary> 9 public class KeywordAnalyzer : Analyzer10 {11 public override TokenStream TokenStream(System.String fieldName, System.IO.TextReader reader)12 {13 return new KeywordTokenizer(reader);14 }1516 public override TokenStream ReusableTokenStream(System.String fieldName, System.IO.TextReader reader)17 {18 Tokenizer tokenizer = (Tokenizer)GetPreviousTokenStream();19 if (tokenizer == null)20 {21 tokenizer = new KeywordTokenizer(reader);22 SetPreviousTokenStream(tokenizer);23 }24 else25 tokenizer.Reset(reader);26 return tokenizer;27 }28 }29}
代码1.2.1.1 就是传说中的源码了。先看看注释,意思大体是“‘Tokenizes’整体的流变成一个个词。这个特别适用于邮编,ID,和商品名称。”Tokenizes应该是拆分的意思,字典上查不到这个词。
这段代码比较简单,只有两个方法,而第二个方法就是我们先前分析结果的时候用的(见段落1.1)。关键点就在于调用了KeywordTokenizer类。切到KeywordTokenizer类查看一下。
代码1.2.1.2
Code 1using System; 2 3namespace Lucene.Net.Analysis 4{ 5 6 /**//// <summary> Emits the entire input as a single token.</summary> 7 public class KeywordTokenizer : Tokenizer 8 { 9 10 private const int DEFAULT_BUFFER_SIZE = 256;11 12 private bool done;13 14 public KeywordTokenizer(System.IO.TextReader input) : this(input, DEFAULT_BUFFER_SIZE)15 {16 }17 18 public KeywordTokenizer(System.IO.TextReader input, int bufferSize) : base(input)19 {20 this.done = false;21 }2223 public override Token Next(Token result)24 {25 if (!done)26 {27 done = true;28 int upto = 0;29 result.Clear();30 char[] buffer = result.TermBuffer();31 while (true)32 {33 int length = input.Read(buffer, upto, buffer.Length - upto);34 if (length <= 0)35 break;36 upto += length;37 if (upto == buffer.Length)38 buffer = result.ResizeTermBuffer(1 + buffer.Length);39 }40 result.termLength = upto;41 return result;42 }43 return null;44 }4546 public override void Reset(System.IO.TextReader input)47 {48 base.Reset(input);49 this.done = false;50 }51 }52}
代码 1.2.1.2 就是KeywordTokenizer的源码。代码量很小,却没有完成全部工作,而是将部分工作交给了父类。关注Lucene的人都可以知道,新版本中,分词这里换掉了,现在多了一个重载的Next方法。这里不讨论为什么要加这个重载,这篇文章主要是讲应用的。因为取词是用Next方法走的,那么只需要关注Next方法就可以了。KeywordTokenizer的父类是Tokenizer,但是在Tokenizer里找不到我们想要的关系,但是Tokenizer又继承自TokenStream。查看TokenStream类。
代码 1.2.1.3
Code 1 2using System; 3 4using Payload = Lucene.Net.Index.Payload; 5 6namespace Lucene.Net.Analysis 7{ 8 9 /**//// <summary>A TokenStream enumerates the sequence of tokens, either from10 /// fields of a document or from query text.11 /// <p>12 /// This is an abstract class. Concrete subclasses are:13 /// <ul>14 /// <li>{@link Tokenizer}, a TokenStream15 /// whose input is a Reader; and16 /// <li>{@link TokenFilter}, a TokenStream17 /// whose input is another TokenStream.18 /// </ul>19 /// NOTE: subclasses must override at least one of {@link20 /// #Next()} or {@link #Next(Token)}.21 /// </summary>22 23 public abstract class TokenStream24 {25 26 /**//// <summary>Returns the next token in the stream, or null at EOS.27 /// The returned Token is a "full private copy" (not28 /// re-used across calls to next()) but will be slower29 /// than calling {@link #Next(Token)} instead.. 30 /// </summary>31 public virtual Token Next()32 {33 Token result = Next(new Token());34 35 if (result != null)36 {37 Payload p = result.GetPayload();38 if (p != null)39 {40 result.SetPayload((Payload) p.Clone());41 }42 }43 44 return result;45 }46 47 /**//// <summary>Returns the next token in the stream, or null at EOS.48 /// When possible, the input Token should be used as the49 /// returned Token (this gives fastest tokenization50 /// performance), but this is not required and a new Token51 /// may be returned. Callers may re-use a single Token52 /// instance for successive calls to this method.53 /// <p>54 /// This implicitly defines a "contract" between 55 /// consumers (callers of this method) and 56 /// producers (implementations of this method 57 /// that are the source for tokens):58 /// <ul>59 /// <li>A consumer must fully consume the previously 60 /// returned Token before calling this method again.</li>61 /// <li>A producer must call {@link Token#Clear()}62 /// before setting the fields in it & returning it</li>63 /// </ul>64 /// Note that a {@link TokenFilter} is considered a consumer.65 /// </summary>66 /// <param name="result">a Token that may or may not be used to return67 /// </param>68 /// <returns> next token in the stream or null if end-of-stream was hit69 /// </returns>70 public virtual Token Next(Token result)71 {72 return Next();73 }74 75 /**//// <summary>Resets this stream to the beginning. This is an76 /// optional operation, so subclasses may or may not77 /// implement this method. Reset() is not needed for78 /// the standard indexing process. However, if the Tokens 79 /// of a TokenStream are intended to be consumed more than 80 /// once, it is necessary to implement reset(). 81 /// </summary>82 public virtual void Reset()83 {84 }85 86 /**//// <summary>Releases resources associated with this stream. </summary>87 public virtual void Close()88 {89 }90 }91}
代码 1.2.1.3 就是TokenStream类的源码。Next(Token)方法和Next()是相互调用的关系。但是因为Next(Token)方法在KeywordTokenizer里被重写掉了,因此,这里就可以忽略TokenStream的Next(Token)方法了。
从上面代码可以看出,调用Next()方法,实际上是传递给Next(Token)方法一个新Token实例。即使直接调用Next(Token),传递一个带有数据的Token,也会先被清除。在循环中,会把构造函数传入的流缓冲进Token类的缓冲区。ResizeTermBuffer方法是自动扩容用的,就像.Net Framework里的一些类能够自然扩容一样。比如List<T>,Hashtable或StringBuilder等。这个过程看不到分词的过程。不过这样就大致明白了分词器工作的流程。
1.2.2 如何让分词器分词
知道分词器如何工作了,但是现在还不明白分词如何分词。再回到1.1.2节,看到WhitespaceAnalyzer分词器似乎是学习的好选择。因为这个分词器只有遇到空格才会进行分词操作。
根据1.2.1的经验,直接查看WhitespaceTokenizer类。
代码1.2.2.1
Code 1using System; 2 3namespace Lucene.Net.Analysis 4{ 5 6 /**//// <summary>A WhitespaceTokenizer is a tokenizer that divides text at whitespace. 7 /// Adjacent sequences of non-Whitespace characters form tokens. 8 /// </summary> 9 10 public class WhitespaceTokenizer : CharTokenizer11 {12 /**//// <summary>Construct a new WhitespaceTokenizer. </summary>13 public WhitespaceTokenizer(System.IO.TextReader in_Renamed) : base(in_Renamed)14 {15 }16 17 /**//// <summary>Collects only characters which do not satisfy18 /// {@link Character#isWhitespace(char)}.19 /// </summary>20 protected internal override bool IsTokenChar(char c)21 {22 return !System.Char.IsWhiteSpace(c);23 }24 }25}
很好,这段代码很短,可是没有看到我们想要的东西。继续看父类。
代码1.2.2.2
Code 1using System; 2 3namespace Lucene.Net.Analysis 4{ 5 6 /**//// <summary>An abstract base class for simple, character-oriented tokenizers.</summary> 7 public abstract class CharTokenizer : Tokenizer 8 { 9 public CharTokenizer(System.IO.TextReader input) : base(input)10 {11 }12 13 private int offset = 0, bufferIndex = 0, dataLen = 0;14 private const int MAX_WORD_LEN = 255;15 private const int IO_BUFFER_SIZE = 1024;16 private char[] ioBuffer = new char[IO_BUFFER_SIZE];17 18 /**//// <summary>Returns true iff a character should be included in a token. This19 /// tokenizer generates as tokens adjacent sequences of characters which20 /// satisfy this predicate. Characters for which this is false are used to21 /// define token boundaries and are not included in tokens. 22 /// </summary>23 protected internal abstract bool IsTokenChar(char c);24 25 /**//// <summary>Called on each token character to normalize it before it is added to the26 /// token. The default implementation does nothing. Subclasses may use this27 /// to, e.g., lowercase tokens. 28 /// </summary>29 protected internal virtual char Normalize(char c)30 {31 return c;32 }3334 public override Token Next(Token token)35 {36 token.Clear();37 int length = 0;38 int start = bufferIndex;39 char[] buffer = token.TermBuffer();40 while (true)41 {4243 if (bufferIndex >= dataLen)44 {45 offset += dataLen;46 dataLen = input is Lucene.Net.Index.DocumentsWriter.ReusableStringReader ? ((Lucene.Net.Index.DocumentsWriter.ReusableStringReader) input).Read(ioBuffer) : input.Read((System.Char[]) ioBuffer, 0, ioBuffer.Length);47 if (dataLen <= 0)48 {49 if (length > 0)50 break;51 else52 return null;53 }54 bufferIndex = 0;55 }5657 char c = ioBuffer[bufferIndex++];5859 if (IsTokenChar(c))60 {61 // if it's a token char6263 if (length == 0)64 // start of token65 start = offset + bufferIndex - 1;66 else if (length == buffer.Length)67 buffer = token.ResizeTermBuffer(1 + length);6869 buffer[length++] = Normalize(c); // buffer it, normalized7071 if (length == MAX_WORD_LEN)72 // buffer overflow!73 break;74 }75 else if (length > 0)76 // at non-Letter w/ chars77 break; // return 'em78 }7980 token.termLength = length;81 token.startOffset = start;82 token.endOffset = start + length;83 return token;84 }8586 public override void Reset(System.IO.TextReader input)87 {88 base.Reset(input);89 bufferIndex = 0;90 offset = 0;91 dataLen = 0;92 }93 }94}
天公不作美,刚看到简单的,就来了个长的。无奈中。不过为什么要多一重继承呢?那就是有其他分词器也用到CharTokenizer了。而WhitespaceTokenizer中没有重写Next方法,而只是重写了IsTokenChar方法,几乎可以肯定。这个IsTokenChar才是重点。IsTokenChar故名思意,一看注释,果然!这个方法是判断是否遇到了分词的点的。这个其实和string类的Split方法相似。注意到Next方法关于IsTokenChar逻辑那一段,恩,果然是这样分词的。实际上就是拆分字符串嘛。
Lucene.Net 2.3.1开发介绍 —— 二、分词(二)的更多相关文章
- Lucene.Net 2.3.1开发介绍 —— 四、搜索(二)
原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(二) 4.3 表达式用户搜索,只会输入一个或几个词,也可能是一句话.输入的语句是如何变成搜索条件的上一篇已经略有提及. 4.3.1 观察 ...
- Lucene.Net 2.3.1开发介绍 —— 三、索引(二)
原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(二) 2.索引中用到的核心类 在Lucene.Net索引开发中,用到的类不多,这些类是索引过程的核心类.其中Analyzer是索引建立的 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(六)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(六) Lucene.Net的上一个版本是2.1,而在2.3.1版本中才引入了Next(Token)方法重载,而ReusableStrin ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(五)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(三)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(四)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyze ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(一)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...
- Lucene.Net 2.3.1开发介绍 —— 三、索引(四)
原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(四) 4.索引对搜索排序的影响 搜索的时候,同一个搜索关键字和同一份索引,决定了一个结果,不但决定了结果的集合,也确定了结果的顺序.那个 ...
- Lucene.Net 2.3.1开发介绍 —— 四、搜索(三)
原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(三) Lucene有表达式就有运算符,而运算符使用起来确实很方便,但另外一个问题来了. 代码 4.3.4.1 Analyzer anal ...
随机推荐
- 进入MFC讲坛的前言(四)
MFC的消息映射机制 MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快.为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息 ...
- ADB logcat 过滤方法(抓取日志)
1. Log信息级别 Log.v- VERBOSE : 黑色 Log.d- DEBUG : 蓝色 Log.i- INFO : 绿色 Log.w- WARN : 橙色 Log.e- ERROR ...
- ASP.NET页面之间传值
介绍: 在网页应用程序的开发中,页面之间的传值应该是最常见的问题了. 在这篇文章里,azamsharp 将为我们介绍一些ASP.NET页面传值的方式.本文所举的例子非常简单,仅仅包含了一个文本框和几个 ...
- Qt同步线程(比较清楚,而且QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition 每个都有例子)
Qt同步线程 我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确.Qt提供了一些类来实现线程的同步,如QMutex,QMute ...
- 服务列表 - Sina App Engine
服务列表 - Sina App Engine 短信服务 新浪无线短信服务是由新浪无线提供的综合性短信服务. 使用服务 下载SDK: php 服务首页 方法 新浪无线短信服务是由新浪无线提供的综合性短信 ...
- Java设计模式菜鸟系列(七)命令模式建模与实现
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39804057 命令模式(Command):将"请求"(命令/口令)封装 ...
- 嵌入jetty到Java代码
在做Demo实例时,使用的jetty版本号为8.x. 为了避免麻烦,将全部的包都导入到MyEclipse的lib文件夹下. 实例1:自己定义handler的服务器 package com.jetty. ...
- PHP - MySQL数据库
第15章 MySQL数据库 学习要点: 1.Web数据库概述 2.MySQL的操作 3.MySQL常用函数 4.SQL语句详解 5.phpMyadmin 一.Web数据库概述 现在,我们已经熟悉了PH ...
- Activity 和 Intent
Activity 和 Intent 一.Intent指向Activity 二.利用 Intent 向第二个 Activity 传数据 三.利用 Intent 接受第二个 Activity 的返回值 四 ...
- ubuntu: root用户
ubuntu怎么设置root用户 http://blog.csdn.net/chenping314159/article/details/7561339 创建root帐号: 在安装系统时,root账 ...