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

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

1.3 分词器结构

1.3.1 分词器整体结构

从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关系图,把TokenStream和他的儿孙们统统拉上去,就能比较好的把握他们之间的关系。

图 1.3.1.1

如图1.3.1.1 就是他们的类关系图。看出如果要做一个分词器,最短的路,就是继承第二代,成为第三代。然后再写一个Analyzer的子类,专门用来做新分词器的适配器就好了。转换器。  呵呵,写Analyzer的过程,就是实践适配器模式的过程。(这里是直接使用了Tokenizer的实例,不能算是适配器模式,更正,感谢老赵指正。 2008年9月1日 2:23:23)

1.3.2 分词器调用流程

光有整体结构还不行,还有了解方法和方法是如何被调用的。还是以最简单的KeywordTokenizer来作为分析对象。

入口毫无疑问,就是KeywordTokenizer的构造函数。然后就是调用Next方法,这是再简单不过的事情。而这里就是要让每次调用Next方法都可以出来一个分词。这个过程可以这么来描述:

(1)、分词,把一句话,一段话或者一篇文章按一个规则划分为N份;

(2)、把这N份片断存储到一个数组中,要同时记录这个片断的内容,还要记录它相对开始位置的偏移;

图 1.3.2.1

(3)、每次调用Next方法,就从上面的数组中取出一个片段;

(4)、片段取完就返回null值;

(5)、发现null值,分词过程结束。

明白以上流程,就可以开始自己写分词器了,嘿嘿。

2.1 自己动手写分词

自己写分词,光知道上面的还不够。自己写分词,首先,你要有个目标,目标是干嘛的呢?就是你这个分词到底是给什么用的,要做到什么程度。比方说,分词是给英文的还是中文的,还是中英文混合,还是还包含了日文。英文的写作规则,单词一空格划分,比较好区分,但是中文的怎么办?归结起来,现在将面临三个问题:

(1)、分词器要分什么东西,怎么才能达到目的,其实就是字符串怎么截取的问题;

(2)、分词器的准确度如何,分词速度如何,怎么做取舍,这是个算法的问题;

(3)、剩下的问题才是如何用Lucene.Net可以理解的方式写出来,这一步,上面讲了那么多小节,却是三个问题的最简单的一个。

2.1.1 最简单的分词方式

这里的最简单指的是用最少代码的方式。好,现在来个最简单的,写成代码2.1.1.1的方式总是最简单了吧?

代码 2.1.1.1

using System;
using Lucene.Net.Analysis;

namespace Test.Analysis
{
    public class

EsayTokenizer : Tokenizer
    {
    }
}

太好了,终于写出来了,下面包装一下,写个Analyzer类。

代码 2.1.1.2

using Lucene.Net.Analysis;

namespace Test.Analysis
{
    public class EsayAnalyzer : Analyzer
    {
        public override TokenStream TokenStream(string fieldName, System.IO.TextReader reader)
        {
            return new

EsayTokenizer();
        }
    }
}

立刻测试一下(测试方法见1.1.1节,具体测试则加入1.1.2节的AllAnalysisTest中,测试代码见 2.1.1.3)。

测试结果:

NUnit的gui崩溃了!!!

这个代码实在太强大了,让测试工具崩溃了!这个问题在1.2.1节讲过,Next方法和Next(Token)方法,在父类中是相互调用的,那会产生什么后果?这就像是个死循环,这个递归永远没办法结束,所以到一定次数以后,会堆栈溢出。所以我们写的分词器必须自己实现一个Next方法,哪怕什么都不做。而另外一个问题就是EsayAnalyzer类总是能拿到一个流,但是现在没办法传到分词器里来,所以,分词器必须有个能传入流的构造函数。对代码修正,如代码2.1.1.3。

代码 2.1.1.3

Code
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using Lucene.Net.Analysis;
 5using System.IO;
 6
 7namespace Test.Analysis
 8{
 9    public class EsayTokenizer : Tokenizer
10    {
11        private TextReader reader;
12
13        public EsayTokenizer(TextReader reader)
14        {
15            this.reader = reader;
16        }
17
18        public override Token Next()
19        {
20            //千万不能调用父类的方法,要不又是死递归
21            //return base.Next();
22            return null;
23        }
24    }
25}

同时把EsayAnalyzer 类对EsayTokenizer类的调用改成return new EsayTokenizer(reader)。OK,测试结果:

Test.Analysis.EsayAnalyzer结果:
--------------------------------
--------------------------------

什么也没有,这个在预料之中。和KeywordTokenizer分词器不同,KeywordTokenizer分词器是什么都有,而且没做任何处理。现在呢是什么都没了。还有一种写法是和这种不相上下的。如代码2.1.1.4。

代码 2.1.14

Code
 1using Lucene.Net.Analysis;
 2
 3namespace Test.Analysis
 4{
 5    public class EsayTooAnalyzer : Analyzer
 6    {
 7        public override TokenStream TokenStream(string fieldName, System.IO.TextReader reader)
 8        {
 9            return new EsayTooTokenizer(reader);
10        }
11    }
12}
13
14using System.IO;
15using Lucene.Net.Analysis;
16
17namespace Test.Analysis
18{
19    public class EsayTooTokenizer : CharTokenizer
20    {
21        public EsayTooTokenizer(TextReader reader)
22            : base(reader)
23        {
24        }
25
26        protected override bool IsTokenChar(char c)
27        {
28            return c == ‘,‘ ? true : false;
29        }
30    }
31}
32

测试结果:

Test.Analysis.EsayTooAnalyzer结果:
--------------------------------
,
--------------------------------

有个逗号了!!!哈哈,总算进步了。而把IsTokenChar方法改一下,改成:

protected override bool IsTokenChar(char c)

{
      return c == ‘,‘ ? false : true;
}

Test.Analysis.EsayTooAnalyzer结果:
--------------------------------
我是中国人,I‘can speak chinese
hello world,沪江小Q!
--------------------------------

这下除了逗号,什么都有了。

改成:

protected override bool IsTokenChar(char c)
 {
        return c == ‘,‘ ? false : false;
 }

就什么都没了,要是两个都是true,会得什么结果呢?

时间: 2024-10-13 01:55:06

Lucene.Net 2.3.1开发介绍 —— 二、分词(三)的相关文章

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

原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只要出现这个字,不管它出现在什么位置.这就产生了上一小节开头讲的,对准确性产生了极大干扰.比如,如果有一段这样的话:“这是一个英雄!他有无法用词汇形容的孤单,但是他并没有用言语来表达.”这句话包含了“英 语 单 词”这四个字,但是却和“英语单词”一点关系都没有.首先想到的解决方法,就是把句子按词来划分

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

原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyzer分词还算比较实用(见1.1.2小节).StandardAnalyzer为什么能满足我们的部分需求,而它又有哪些不足呢?看分词的好坏还是要从效果说起.简单的说,在中英文混合的情况下,StandardAnalyzer会把英文按空格拆,而中文则按单字拆.因为中文是按单字拆,所以对分词的准确性起到了干扰,

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

原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(二) 4.3 表达式用户搜索,只会输入一个或几个词,也可能是一句话.输入的语句是如何变成搜索条件的上一篇已经略有提及. 4.3.1 观察表达式在研究表达式之前,一定要知道,任何一个Query都会对于一个表达式.不光可以通过Query构造表达式,还可以通过拼接字符串构造.这里说的观察表达式是指,用Query完成查询语句后,用ToString()方法输出Query的表达式.很简单是吧,呵呵. 4.3.2 表达式的与或非“与或非”让我想起

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

原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(二) 2.索引中用到的核心类 在Lucene.Net索引开发中,用到的类不多,这些类是索引过程的核心类.其中Analyzer是索引建立的基础,Directory是索引建立中或者建立好存储的介质,Document和Field类是逻辑结构的核心,IndexWriter是操作的核心.其他类的使用都被隐藏掉了,这也是为什么Lucene.Net使用这么方便的原因. 2.1 Analyzer 前面已经对Analyzer进行了很详细的讲解,Ana

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

原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(四) 4.索引对搜索排序的影响 搜索的时候,同一个搜索关键字和同一份索引,决定了一个结果,不但决定了结果的集合,也确定了结果的顺序.那个这个结果是怎么得出来的?这个顺序又是怎么排的呢?这两个问题不是本节讨论的重点,但是这两个问题却关系到本节要讨论的,索引对结果的影响问题.在不使用字段排序的情况下,Lucene.Net默认是按文档的得分来排序的,这个公式看着很复杂,感觉像是大学时高数书上的那些个公式,其实说清楚了也简单. 关于文档排序

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

原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(五) 话接上篇,继续来说权重对排序的影响.从上面的4个测试,只能说是有个直观的理解了.“哦,是!调整权重是能影响排序了,但是好像没办法来分析到底怎么调啊!”.似乎是这样,现在需要把问题放大,加大索引的内容.到博客园新闻区,用zzk找了4篇内容包含“测试”的文章.代码变成 2.1.5 代码2.1.5  1using System;  2using System.Collections.Generic;  3using Lucene.N

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

原文:Lucene.Net 2.3.1开发介绍 -- 三.索引(七) 5.IndexWriter 索引这部分最后讲的是IndexWriter.如果说前面提到的都是数据的结构,那么IndexWriter就是业务的封装.无论述Document,Field还是看不见的Segment,Term都是对数据存储逻辑的抽象,IndexWriter包装了操作的过程. 当然,这里不会讨论IndexWriter的每个细节,这里主要介绍IndexWriter的常用法和实际使用中遇到的部署问题. 5.1 IndexWr

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

原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(三) Lucene有表达式就有运算符,而运算符使用起来确实很方便,但另外一个问题来了. 代码 4.3.4.1 Analyzer analyzer = new StandardAnalyzer(); QueryParser parser = new QueryParser("title", analyzer); Query query = parser.Parse(@":"); Console.Write

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

原文:Lucene.Net 2.3.1开发介绍 -- 四.搜索(一) 既然是内容筛选,或者说是搜索引擎,有索引,必然要有搜索.搜索虽然与索引有关,那也只是与索引后的文件有关,和索引的程序是无关的,因此,搜索和索引一般是分开部署.简单地说,就是一个应用程序(桌面程序)来索引,一个WEB程序来实现搜索.当然,为了测试的时候简单,这里还是使用NUnit的方式运行.搜索讲完后,将会简单介绍单机搜索引擎如何部署. 4.1 搜索与什么有关 搜索与什么有关呢?即使没有看过前面的文章,那么现在来随便猜一猜. 首