原文: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开发介绍 —— 二、分词(二)的更多相关文章

  1. Lucene.Net 2.3.1开发介绍 —— 四、搜索(二)

    原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(二) 4.3 表达式用户搜索,只会输入一个或几个词,也可能是一句话.输入的语句是如何变成搜索条件的上一篇已经略有提及. 4.3.1 观察 ...

  2. Lucene.Net 2.3.1开发介绍 —— 三、索引(二)

    原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(二) 2.索引中用到的核心类 在Lucene.Net索引开发中,用到的类不多,这些类是索引过程的核心类.其中Analyzer是索引建立的 ...

  3. Lucene.Net 2.3.1开发介绍 —— 二、分词(六)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(六) Lucene.Net的上一个版本是2.1,而在2.3.1版本中才引入了Next(Token)方法重载,而ReusableStrin ...

  4. Lucene.Net 2.3.1开发介绍 —— 二、分词(五)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只 ...

  5. Lucene.Net 2.3.1开发介绍 —— 二、分词(三)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...

  6. Lucene.Net 2.3.1开发介绍 —— 二、分词(四)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyze ...

  7. Lucene.Net 2.3.1开发介绍 —— 二、分词(一)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...

  8. Lucene.Net 2.3.1开发介绍 —— 三、索引(四)

    原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(四) 4.索引对搜索排序的影响 搜索的时候,同一个搜索关键字和同一份索引,决定了一个结果,不但决定了结果的集合,也确定了结果的顺序.那个 ...

  9. Lucene.Net 2.3.1开发介绍 —— 四、搜索(三)

    原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(三) Lucene有表达式就有运算符,而运算符使用起来确实很方便,但另外一个问题来了. 代码 4.3.4.1 Analyzer anal ...

随机推荐

  1. linux下C++开发工具

    就C++开发工具而言,与Windows下微软(VC, VS2005等)一统天下相比,Linux/Unix下C++开发,可谓五花八门,各式各样.Emacs, vi, eclipse, anjuta,kd ...

  2. 进入MFC讲坛的前言(三)

    MFC中的窗口创建及窗口消息映射 我经常碰到有人问我有关窗口创建的问题,他们经常把用HWND描述的系统窗口对象和用CWnd描述的MFC的窗口对象混淆不清.这两者之间是紧密联系在一起的,但是MFC为了自 ...

  3. HDU ACM 1066 Last non-zero Digit in N!

    #include<iostream> using namespace std; int mod[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2}; ...

  4. Android 实现在线程中联网

    其实我们要牢记的是,对数据流的操作都是阻塞的,在一般情况下,我们是不需要考虑这个问题的,但是在Android 实现联网的时候,我们必须考虑到这个问题.比如:从网络上下载一张图片: Java代码: pu ...

  5. Linux之shell编程基础

    一.变量 变量在shell中分为:本地变量.环境变量.位置参数: 本地变量:仅可在用户当前shell生命期的脚本中使用的变量,本地变量随着shell进程的消亡而无效,本地变量在新启动的shell中依旧 ...

  6. BZOJ 1009: [HNOI2008]GT考试( dp + 矩阵快速幂 + kmp )

    写了一个早上...就因为把长度为m的也算进去了... dp(i, j)表示准考证号前i个字符匹配了不吉利数字前j个的方案数. kmp预处理, 然后对于j进行枚举, 对数字0~9也枚举算出f(i, j) ...

  7. WPF Multi-Touch 开发:高效开发模式

    原文 WPF Multi-Touch 开发:高效开发模式 在前几篇文章中已经介绍了触屏操作的多种模式,并对其开发方式也有了进一步了解.细心的朋友应该会发现在上一篇文章中,如果拖动图片过快它会因惯性效果 ...

  8. pytesser图片文本识别

    python图片文本识别使用的工具是PIL和pytesser.因为他们使用到很多的python库文件,为了避免一个个工具的安装,建议使用pythonxy,这个工具的介绍可参考baidu. pytess ...

  9. HDU4544 湫湫系列故事――消灭兔子

    HDU 4544 Tags: 数据结构,贪心 Analysis: 将兔子的血量从大到小排序,将箭的杀伤力从大到小排序,对于每一个兔子血量, 将比他大的杀伤力大的剑压入优先队列,优先队列自己重写,让它每 ...

  10. three.js 源代码凝视(十)Math/Line3.js

    商域无疆 (http://blog.csdn.net/omni360/) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:商域无疆 -  本博客专注于 敏捷开发 ...