1 | [![jetbrains.svg](jetbrains.svg)](https://www.jetbrains.com/?from=novel-segment)
|
2 |
|
3 | # 中文分词模块
|
4 |
|
5 | Chinese word segmentation 簡繁中文分词模块 以網路小說為樣本
|
6 |
|
7 | 本模块以**[盘古分词组件](http://pangusegment.codeplex.com/)**中的词库为基础,
|
8 | 算法设计也部分参考了盘古分词组件中的算法。
|
9 |
|
10 | 本分词模块具有以下特点:
|
11 |
|
12 | + 纯JavaScript编写,可以在任何支持ECMAScript5的引擎上执行(需要稍微修改部分代码)
|
13 | + 基于词性进行联想识别
|
14 | + 可使用JavaScript编写自定义的分词模块
|
15 |
|
16 | Fork From [leizongmin/segment](https://github.com/leizongmin/node-segment)
|
17 |
|
18 | ---
|
19 |
|
20 | 1. 以網路翻譯小說為樣本增加字典
|
21 | 2. 可緩存字典數據讓下次使用勉強快一丁點
|
22 | 3. 可啟用自動將字典無視簡繁日漢字
|
23 | 4. 精簡一部分多餘字典
|
24 | 5. 可額外追加字典條目而不需要增加字典檔
|
25 | 6. 可將結果轉換為原始格式
|
26 | 7. 遇到長句,無分段,無標點符號的行時會捨棄部分處理,來避免處理時間過長過超過記憶體負荷<br/>可於啟動 nodejs 時 加上參數 例如 `node --max-old-space-size=2048 xxxx.js` 可以避免記憶體洩漏問題
|
27 | 8. 與原版不同預設會返回所有字元(包含分行與空格)
|
28 |
|
29 | * [線上測試 by RunKit](https://npm.runkit.com/novel-segment)
|
30 |
|
31 | ## live demo
|
32 |
|
33 | > https://segment-api.bluelovers.now.sh/demo.html
|
34 |
|
35 | ### api
|
36 |
|
37 | ```
|
38 | https://segment-api.bluelovers.now.sh/conv?input=从时间上来说是过了数秒。视线的焦点总算稳定后,那是在顶棚略低的运动跑车的右边驾驶席上。稳稳坐在车内驾驶席上的要,朝着映在车内镜的脸庞望去。柔顺的黑发加上好战的锐利目光,穿在身上的松垮衬衫被卷起了袖子,系在脖子上的红色领带显得极为松垮。把视线落到双手上,带着手表的手腕下是不怎么厚的蓝色裤子,腿上还挂着黑色的随身包。一瞬间脑子还有点没转过来,之后总算理解了情况。
|
39 | ```
|
40 |
|
41 | ```
|
42 | https://segment-api.bluelovers.now.sh/?input=从时间上来说是过了数秒。视线的焦点总算稳定后,那是在顶棚略低的运动跑车的右边驾驶席上。稳稳坐在车内驾驶席上的要,朝着映在车内镜的脸庞望去。柔顺的黑发加上好战的锐利目光,穿在身上的松垮衬衫被卷起了袖子,系在脖子上的红色领带显得极为松垮。把视线落到双手上,带着手表的手腕下是不怎么厚的蓝色裤子,腿上还挂着黑色的随身包。一瞬间脑子还有点没转过来,之后总算理解了情况。
|
43 | ```
|
44 |
|
45 | **歡迎一同來追加字典**
|
46 |
|
47 | * [segment-dict](https://github.com/bluelovers/ws-segment/tree/master/packages/segment-dict) - dictionary data
|
48 |
|
49 | ### TODO
|
50 |
|
51 | > 以下功能距離實現可能遙遙無期
|
52 |
|
53 | 1. 追加支援常見混雜於中文內的英文辭典
|
54 |
|
55 | ## Breaking Changes
|
56 |
|
57 | 請注意 從 2.0.0 版之後 開始 更改了 synonym 字典的格式
|
58 |
|
59 | * 原版為一對一 => 錯字,正字
|
60 | * 這裡為一對多 並且順序與原版相反 => 正字,錯字,...以,分隔更多字
|
61 |
|
62 | 請注意 從 2.2.0 版之後 開始 更改了分詞算法
|
63 |
|
64 | * 當分詞得分相同時會以先出現的結果為優先
|
65 | * 修正語法計算錯誤 { `形容詞 + 動詞` => `副詞 + 動詞` }
|
66 |
|
67 |
|
68 | ## 安装
|
69 |
|
70 | ```bash
|
71 | npm install novel-segment
|
72 | ```
|
73 |
|
74 | * npm: [novel-segment](https://www.npmjs.com/package/novel-segment)
|
75 | * github: [novel-segment](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment)
|
76 |
|
77 | ## demo
|
78 |
|
79 | 以下範例已經自動啟用以下功能
|
80 |
|
81 | * 新增字典項目時自動補充繁簡日的異體漢字
|
82 | * 啟用 ZhtSynonymOptimizer 模組
|
83 | * 緩存功能
|
84 |
|
85 | * [demo.glob.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/node-segment/tree/master/test/demo.glob.ts)
|
86 | * [demo.cache.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/node-segment/tree/master/test/demo.cache.ts)
|
87 |
|
88 | 可搭配其他繁簡轉換程式使用
|
89 |
|
90 | * [線上測試 by RunKit](https://npm.runkit.com/novel-segment)
|
91 |
|
92 | ## API
|
93 |
|
94 | * [API](docs)
|
95 | * [其他雜項 Readme](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/demo)
|
96 | * [Segment.d.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/lib/Segment.d.ts)
|
97 | * [POSTAG.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/lib/POSTAG.ts)
|
98 | * [segment-dict](https://github.com/bluelovers/ws-segment/tree/master/packages/segment-dict) - 字典 dictionary data
|
99 |
|
100 | ### 特點模組
|
101 |
|
102 | * [ZhtSynonymOptimizer.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/lib/submod/ZhtSynonymOptimizer.ts) - 基於語意來修正各種需要人工修正的詞彙 例如 `里后`...等等 (預設不啟用 因為這與分詞無關)
|
103 | * [JpSimpleTokenizer.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/lib/submod/JpSimpleTokenizer.ts) 簡易的日文切割 (預設啟用)
|
104 | * [ForeignOptimizer.ts](https://github.com/bluelovers/ws-segment/tree/master/packages/novel-segment/tree/master/lib/submod/ForeignOptimizer.ts) 合併外文與中文混雜的詞 (預設啟用)
|
105 |
|
106 | ## 1、使用方法
|
107 |
|
108 | 使用方法:
|
109 |
|
110 | ```javascript
|
111 | // 载入模块
|
112 | var Segment = require('novel-segment');
|
113 | // 创建实例
|
114 | var segment = new Segment();
|
115 | // 使用默认的识别模块及字典,载入字典文件需要1秒,仅初始化时执行一次即可
|
116 | segment.useDefault();
|
117 |
|
118 | // 开始分词
|
119 | console.log(segment.doSegment('这是一个基于Node.js的中文分词模块。'));
|
120 | ```
|
121 |
|
122 | 返回结果格式:
|
123 |
|
124 | ```javascript
|
125 | [ { w: '这是', p: 0 },
|
126 | { w: '一个', p: 2097152 },
|
127 | { w: '基于', p: 262144 },
|
128 | { w: 'Node.js', p: 8 },
|
129 | { w: '的', p: 8192 },
|
130 | { w: '中文', p: 1048576 },
|
131 | { w: '分词', p: 4096 },
|
132 | { w: '模块', p: 1048576 },
|
133 | { w: '。', p: 2048 } ]
|
134 | ```
|
135 | 其中 `w` 表示词的内容,`p` 表示词性(具体参考 https://github.com/bluelovers/node-segment/blob/master/lib/POSTAG.ts 中的定义)
|
136 |
|
137 | ### 不返回词性
|
138 |
|
139 | ```javascript
|
140 | var text = '这是一个基于Node.js的中文分词模块。';
|
141 | var result = segment.doSegment(text, {
|
142 | simple: true
|
143 | });
|
144 | console.log(result);
|
145 | ```
|
146 |
|
147 | 结果:
|
148 |
|
149 | ```javascript
|
150 | [ '这是', '一个', '基于', 'Node.js', '的', '中文', '分词', '模块', '。' ]
|
151 | ```
|
152 |
|
153 | ### 去除标点符号
|
154 |
|
155 | ```javascript
|
156 | var text = '这是一个基于Node.js的中文分词模块。';
|
157 | var result = segment.doSegment(text, {
|
158 | stripPunctuation: true
|
159 | });
|
160 | console.log(result);
|
161 | ```
|
162 |
|
163 | 结果:
|
164 |
|
165 | ```javascript
|
166 | [ { w: '这是', p: 0 },
|
167 | { w: '一个', p: 2097152 },
|
168 | { w: '基于', p: 262144 },
|
169 | { w: 'Node.js', p: 8 },
|
170 | { w: '的', p: 8192 },
|
171 | { w: '中文', p: 1048576 },
|
172 | { w: '分词', p: 4096 },
|
173 | { w: '模块', p: 1048576 } ]
|
174 | ```
|
175 |
|
176 | ### 转换同义词
|
177 |
|
178 | 载入同义词词典:
|
179 |
|
180 | ```javascript
|
181 | segment.loadSynonymDict('synonym.txt');
|
182 | ```
|
183 |
|
184 | 词典格式:
|
185 |
|
186 | ```
|
187 | 什么时候,何时
|
188 | 入眠,入睡
|
189 | ```
|
190 |
|
191 | 在分词时设置`convertSynonym=true`则结果中的`"什么时候"`将被转换为`"何时"`,`"入眠"`将被转换为`"入睡"`:
|
192 |
|
193 | ```javascript
|
194 | var text = '什么时候我也开始夜夜无法入睡';
|
195 | var result = segment.doSegment(text, {
|
196 | convertSynonym: true
|
197 | });
|
198 | console.log(result);
|
199 | ```
|
200 |
|
201 | 结果:
|
202 |
|
203 | ```javascript
|
204 | [ { w: '何时', p: 0 },
|
205 | { w: '我', p: 65536 },
|
206 | { w: '也', p: 134217728 },
|
207 | { w: '开始', p: 4096 },
|
208 | { w: '夜夜', p: 131072 },
|
209 | { w: '无法', p: 134217728 },
|
210 | { w: '入睡', p: 4096 } ]
|
211 | ```
|
212 |
|
213 | ### 去除停止符
|
214 |
|
215 | 载入词典:
|
216 |
|
217 | ```javascript
|
218 | segment.loadStopwordDict('stopword.txt');
|
219 | ```
|
220 |
|
221 | 词典格式:
|
222 |
|
223 | ```
|
224 | 之所以
|
225 | 因为
|
226 | ```
|
227 |
|
228 | 在分词时设置`stripStopword=true`则结果中的`"之所以"`和`"因为"`将被去除:
|
229 |
|
230 | ```javascript
|
231 | var text = '之所以要编写一个纯JS的分词器是因为当时没有一个简单易用的Node.js模块';
|
232 | var result = segment.doSegment(text, {
|
233 | stripStopword: true
|
234 | });
|
235 | console.log(result);
|
236 | ```
|
237 |
|
238 | 结果:
|
239 |
|
240 | ```javascript
|
241 | [ { w: '编写', p: 4096 },
|
242 | { w: '纯', p: 1073741824 },
|
243 | { w: 'JS', p: [ 16 ] },
|
244 | { w: '分词', p: 4096 },
|
245 | { w: '器' },
|
246 | { w: '当时', p: 16384 },
|
247 | { w: '没有', p: 4096 },
|
248 | { w: '简单', p: 1073741824 },
|
249 | { w: '易用' },
|
250 | { w: 'Node.js', p: 8 },
|
251 | { w: '模块', p: 1048576 } ]
|
252 | ```
|
253 |
|
254 |
|
255 | ## 2、词典格式
|
256 |
|
257 | 词典文件为纯文本文件,每行定义一个词,格式为: `词|词性|词权值` ,如:`工信处|0x0020|100`
|
258 |
|
259 | **词性** 的定义可参考文件 https://github.com/bluelovers/node-segment/blob/master/lib/POSTAG.ts
|
260 |
|
261 | **词权值** 越大表示词出现的频率越高
|
262 |
|
263 | 词典文件可参考:https://github.com/bluelovers/node-segment/tree/master/dicts
|
264 |
|
265 |
|
266 | ## 2、自定义识别模块
|
267 |
|
268 | ```javascript
|
269 | // 载入模块
|
270 | var Segment = require('novel-segment');
|
271 | // 创建实例
|
272 | var segment = new Segment();
|
273 | // 配置,可根据实际情况增删,详见segment.useDefault()方法
|
274 | segment.use('URLTokenizer'); // 载入识别模块,详见lib/module目录,或者是自定义模块的绝对路径
|
275 | segment.loadDict('dict.txt'); // 载入字典,详见dicts目录,或者是自定义字典文件的绝对路径
|
276 |
|
277 | // 开始分词
|
278 | console.log(segment.doSegment('这是一个基于Node.js的中文分词模块。'));
|
279 | ```
|
280 |
|
281 | 一般可通过 `segment.useDefault()` 来载入默认的配置,若要自定义加载,可参考 `useDefault()` 的代码:
|
282 |
|
283 | ```javascript
|
284 | segment
|
285 | // 分词模块
|
286 | // 强制分割类单词识别
|
287 | .use('URLTokenizer') // URL识别
|
288 | .use('WildcardTokenizer') // 通配符,必须在标点符号识别之前
|
289 | .use('PunctuationTokenizer') // 标点符号识别
|
290 | .use('ForeignTokenizer') // 外文字符、数字识别,必须在标点符号识别之后
|
291 | // 中文单词识别
|
292 | .use('DictTokenizer') // 词典识别
|
293 | .use('ChsNameTokenizer') // 人名识别,建议在词典识别之后
|
294 |
|
295 | // 优化模块
|
296 | .use('EmailOptimizer') // 邮箱地址识别
|
297 | .use('ChsNameOptimizer') // 人名识别优化
|
298 | .use('DictOptimizer') // 词典识别优化
|
299 | .use('DatetimeOptimizer') // 日期时间识别优化
|
300 |
|
301 | // 字典文件
|
302 | .loadDict('dict.txt') // 盘古词典
|
303 | .loadDict('dict2.txt') // 扩展词典(用于调整原盘古词典)
|
304 | .loadDict('names.txt') // 常见名词、人名
|
305 | .loadDict('wildcard.txt', 'WILDCARD', true) // 通配符
|
306 | .loadSynonymDict('synonym.txt') // 同义词
|
307 | .loadStopwordDict('stopword.txt') // 停止符
|
308 | ```
|
309 |
|
310 | 自定义分词器:
|
311 |
|
312 | ```javascript
|
313 | segment.use({
|
314 |
|
315 | // 类型
|
316 | type: 'tokenizer',
|
317 |
|
318 | // segment.use() 载入模块,初始化时执行
|
319 | init: function (segment) {
|
320 | // segment 为当前的Segment实例
|
321 | },
|
322 |
|
323 | // 分词
|
324 | split: function (words) {
|
325 | // words 为单词数组,如:['中文', '分词']
|
326 | // 返回一个新的数组用来替换旧的数组
|
327 | return words;
|
328 | }
|
329 |
|
330 | });
|
331 | ```
|
332 |
|
333 | 自定义优化器:
|
334 |
|
335 | ```javascript
|
336 | segment.use({
|
337 |
|
338 | // 类型
|
339 | type: 'optimizer',
|
340 |
|
341 | // segment.use() 载入模块,初始化时执行
|
342 | init: function (segment) {
|
343 | // segment 为当前的Segment实例
|
344 | },
|
345 |
|
346 | // 优化
|
347 | doOptimize: function (words) {
|
348 | // words 为分词结果的单词数组,如:[{w: '中文', p: 1048576}, {w: '分词', p: 4096}]
|
349 | // 返回一个新的数组用来替换旧的数组
|
350 | return words;
|
351 | }
|
352 |
|
353 | })
|
354 | ```
|
355 |
|
356 | 分词器和优化器可参考默认模块:https://github.com/bluelovers/node-segment/tree/master/lib/module
|
357 |
|
358 | 其中 `*Tokenizer` 表示分词器, `*Optimizer` 表示优化器。
|
359 |
|
360 |
|
361 | ## 注意
|
362 |
|
363 | **请勿用此模块来对较长且无任何标点符号的文本进行分词,否则会导致分词时间成倍增加。**
|
364 |
|
365 |
|
366 | ## MIT License
|
367 |
|
368 | ```
|
369 | Copyright (c) 2012-2015 Zongmin Lei (雷宗民) <leizongmin@gmail.com>
|
370 | http://ucdok.com
|
371 |
|
372 | The MIT License
|
373 |
|
374 | Permission is hereby granted, free of charge, to any person obtaining
|
375 | a copy of this software and associated documentation files (the
|
376 | "Software"), to deal in the Software without restriction, including
|
377 | without limitation the rights to use, copy, modify, merge, publish,
|
378 | distribute, sublicense, and/or sell copies of the Software, and to
|
379 | permit persons to whom the Software is furnished to do so, subject to
|
380 | the following conditions:
|
381 |
|
382 | The above copyright notice and this permission notice shall be
|
383 | included in all copies or substantial portions of the Software.
|
384 |
|
385 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
386 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
387 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
388 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
389 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
390 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
391 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
392 | ```
|
393 |
|