原文:Lucene.Net 2.3.1开发介绍 —— 二、分词(五)
2.1.3 二元分词
****
上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只要出现这个字,不管它出现在什么位置。这就产生了上一小节开头讲的,对准确性产生了极大干扰。比如,如果有一段这样的话:“这是一个
英雄!他有无法用
词汇形容的孤
单,但是他并没有用言
语来表达。”这句话包含了“英 语 单 词”这四个字,但是却和“英语单词”一点关系都没有。首先想到的解决方法,就是把句子按词来划分,那么就能有效的降低干扰。最简单的解决方法,莫过于每两个字组成一个部分。
下面来构造核心算法。首先我们期望,只有中文(广义上指双字节文字,比如日文,韩文也在这个范围。)是按照二元拆分,而符号则是单符号拆分,对于英文则保持原样。因此,需要一个判断当前字符类型的函数。首先,构造一个枚举,如代码2.1.3.1。
代码 2.1.3.1
Code
///
<summary>
///
Char类型枚举,用于分词中类型状态比较
///
</summary>
public
enum
CharType
{
None,
//
默认值,不可识别类型
English,
//
拉丁字符,用英文标识
Chinese,
//
CJK字符,以中文代表
Number,
//
阿拉伯数字
Control
//
控制符号,指控制符号已经各种标点符号等
}
接下来需要有一个函数能够识别字符,把字符类型转换成我们需要的CharType。
代码 2.1.3.2
Code
1
/**/
///
<summary>
2
///
获取Char类型
3
///
</summary>
4
///
<param name="c">
字符
</param>
5
///
<returns>
返回类型
</returns>
6
public
static
CharType GetCharType(
char
c)
7
{
8
switch
(
char
.GetUnicodeCategory(c))
9
{
10
//
大小写字符判断为英文字符
11
case
System.Globalization.UnicodeCategory.UppercaseLetter:
12
case
System.Globalization.UnicodeCategory.LowercaseLetter:
13
return
CharType.English;
14
//
其它字符判断问中文(CJK)
15
case
System.Globalization.UnicodeCategory.OtherLetter:
16
return
CharType.Chinese;
17
//
十进制数字
18
case
System.Globalization.UnicodeCategory.DecimalDigitNumber:
19
return
CharType.Number;
20
//
其他都认为是符号
21
default
:
22
return
CharType.Control;
23
}
24
}
代码2.1.3.2粗略完成了我们想要的功能。现在就可以构造我们想要的算法了。
代码 2.1.3.3
Code
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Text;
4
using
Lucene.Net.Analysis;
5
using
System.IO;
6
7
namespace
Test.Analysis
8
{
9
public
class
DoubleTokenizer : Tokenizer
10
{
11
/**/
///
<summary>
12
///
保持传入的流
13
///
</summary>
14
private
TextReader reader;
15
/**/
///
<summary>
16
///
控制分词器只打开一次
17
///
</summary>
18
private
bool
done
true
;
19
/**/
///
<summary>
20
///
保存分词结果
21
///
</summary>
22
private
List
<
Token
tokenlist;
23
24
public
DoubleTokenizer(TextReader reader)
25
{
26
this
.reader
reader;
27
}
28
/**/
///
<summary>
29
///
上一个字的类型
30
///
</summary>
31
private
CharType lastype
CharType.None;
32
/**/
///
<summary>
33
///
当前读取到分词的记录数
34
///
</summary>
35
private
int
ptr
0
;
36
/**/
///
<summary>
37
///
重写Next方法
38
///
</summary>
39
///
<param name="result"></param>
40
///
<returns></returns>
41
public
override
Token Next(Token result)
42
{
43
if
(done)
//
第一次可以运行,运行后将被设置为false,在一个实例中只会运行一次
44
{
45
done
false
;
46
string
text
reader.ReadToEnd();
47
//
输入为空,则返回结束符号
48
if
(
string
.IsNullOrEmpty(text))
49
return
null
;
50
//
初始化分词结果
51
tokenlist
new
List
<
Token
();
52
//
缓冲器,主要用于暂时保存英文数字字符。
53
StringBuilder buffer
new
StringBuilder();
54
Token token;
55
for
(
int
i
0
; i
<
text.Length; i
++
)
56
{
57
char
nowchar
text[i];
58
char
nextchar
new
char
();
59
CharType nowtype
GetCharType(nowchar);
60
if
(i
<
text.Length
1
)
//
取下一个字符
61
nextchar
text[i
+
1
];
62
//
状态转换
63
if
(nowtype
!=
lastype)
64
{
65
lastype
nowtype;
66
if
(buffer.Length
0
)
67
{
68
token
new
Token(buffer.ToString(), i
buffer.Length, i);
69
tokenlist.Add(token);
70
buffer.Remove(
0
, buffer.Length);
71
}
72
}
73
74
switch
(nowtype)
75
{
76
case
CharType.None:
77
case
CharType.Control:
78
goto
SingleChar;
79
case
CharType.Chinese:
80
break
;
81
case
CharType.English:
82
case
CharType.Number:
83
buffer.Append(nowchar);
84
continue
;
85
}
86
//
处理连续两个中文字符
87
if
(GetCharType(nextchar)
CharType.Chinese)
88
{
89
token
new
Token(nowchar.ToString()
+
nextchar.ToString(), i, i
+
2
);
90
tokenlist.Add(token);
91
i
++
;
92
continue
;
93
}
94
95
SingleChar:
//
处理单个字符
96
token
new
Token(nowchar.ToString(), i, i
+
1
);
97
tokenlist.Add(token);
98
continue
;
99
}
100
//
返回第一个分词结果,并且把指针移向下一位
101
return
tokenlist[ptr
++
];
102
}
103