Restore 0.1.5 version from stash
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""最大正向匹配分词"""
|
||||
from pypinyin.constants import PHRASES_DICT
|
||||
|
||||
|
||||
class Seg(object):
|
||||
"""正向最大匹配分词
|
||||
|
||||
:type prefix_set: PrefixSet
|
||||
:param no_non_phrases: 是否严格按照词语分词,不允许把非词语的词当做词语进行分词
|
||||
:type no_non_phrases: bool
|
||||
"""
|
||||
|
||||
def __init__(self, prefix_set, no_non_phrases=False):
|
||||
self._prefix_set = prefix_set
|
||||
self._no_non_phrases = no_non_phrases
|
||||
|
||||
def cut(self, text):
|
||||
"""分词
|
||||
|
||||
:param text: 待分词的文本
|
||||
:yield: 单个词语
|
||||
"""
|
||||
remain = text
|
||||
while remain:
|
||||
matched = ''
|
||||
last_valid_word = ''
|
||||
last_valid_index = 0
|
||||
|
||||
# 一次加一个字的匹配
|
||||
for index in range(len(remain)):
|
||||
word = remain[:index + 1]
|
||||
if word in self._prefix_set:
|
||||
matched = word
|
||||
# 检查当前匹配的词是否为有效词语
|
||||
if (not self._no_non_phrases) or word in PHRASES_DICT:
|
||||
last_valid_word = word
|
||||
last_valid_index = index + 1
|
||||
else:
|
||||
# 前缀匹配失败,需要处理之前的匹配结果
|
||||
if last_valid_word:
|
||||
# 有有效词语,输出最后一个有效词语
|
||||
yield last_valid_word
|
||||
remain = remain[last_valid_index:]
|
||||
else:
|
||||
# 没有有效词语
|
||||
if self._no_non_phrases:
|
||||
# 严格模式:输出第一个字符
|
||||
yield remain[0]
|
||||
remain = remain[1:]
|
||||
else:
|
||||
# 非严格模式:输出匹配到的前缀(如果有)或第一个字符
|
||||
if matched:
|
||||
yield matched
|
||||
remain = remain[len(matched):]
|
||||
else:
|
||||
yield remain[0]
|
||||
remain = remain[1:]
|
||||
break
|
||||
else: # 整个剩余文本都能匹配前缀
|
||||
if last_valid_word:
|
||||
# 有有效词语,输出最后一个有效词语
|
||||
yield last_valid_word
|
||||
remain = remain[last_valid_index:]
|
||||
else:
|
||||
# 没有有效词语,处理剩余文本
|
||||
if self._no_non_phrases and remain not in PHRASES_DICT:
|
||||
# 严格模式且不在词典中:拆分为单字符
|
||||
for x in remain:
|
||||
yield x
|
||||
else:
|
||||
# 非严格模式或在词典中:输出整个剩余文本
|
||||
yield remain
|
||||
break
|
||||
|
||||
def train(self, words):
|
||||
"""训练分词器
|
||||
|
||||
:param words: 词语列表
|
||||
"""
|
||||
self._prefix_set.train(words)
|
||||
|
||||
|
||||
class PrefixSet(object):
|
||||
def __init__(self):
|
||||
self._set = set()
|
||||
|
||||
def train(self, word_s):
|
||||
"""更新 prefix set
|
||||
|
||||
:param word_s: 词语库列表
|
||||
:type word_s: iterable
|
||||
:return: None
|
||||
"""
|
||||
for word in word_s:
|
||||
# 把词语的每个前缀更新到 prefix_set 中
|
||||
for index in range(len(word)):
|
||||
self._set.add(word[:index + 1])
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._set
|
||||
|
||||
|
||||
p_set = PrefixSet()
|
||||
p_set.train(PHRASES_DICT.keys())
|
||||
|
||||
#: 基于内置词库的最大正向匹配分词器。使用:
|
||||
#:
|
||||
#: .. code-block:: python
|
||||
#:
|
||||
#: >>> from pypinyin.contrib.mmseg import seg
|
||||
#: >>> text = '你好,我是中国人,我爱我的祖国'
|
||||
#: >>> seg.cut(text)
|
||||
#: <generator object Seg.cut at 0x10b2df2b0>
|
||||
#: >>> list(seg.cut(text))
|
||||
#: ['你好', ',', '我', '是', '中国人', ',', '我', '爱',
|
||||
#: '我的', '祖', '国']
|
||||
#: >>> seg.train(['祖国', '我是'])
|
||||
#: >>> list(seg.cut(text))
|
||||
#: ['你好', ',', '我是', '中国人', ',', '我', '爱',
|
||||
#: '我的', '祖国']
|
||||
#: >>>
|
||||
seg = Seg(p_set, no_non_phrases=True)
|
||||
|
||||
|
||||
def retrain(seg_instance):
|
||||
"""重新使用内置词典训练 seg_instance。
|
||||
|
||||
比如在增加自定义词语信息后需要调用这个模块重新训练分词器
|
||||
|
||||
:type seg_instance: Seg
|
||||
"""
|
||||
seg_instance.train(PHRASES_DICT.keys())
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import Iterator
|
||||
from typing import Text
|
||||
from typing import Set
|
||||
|
||||
|
||||
class Seg(object):
|
||||
"""最大正向匹配分词
|
||||
|
||||
:type prefix_set: PrefixSet
|
||||
:type no_non_phrases: bool
|
||||
"""
|
||||
def __init__(self, prefix_set: PrefixSet, no_non_phrases: bool = False) -> None:
|
||||
self._no_non_phrases = ... # type: bool
|
||||
self._prefix_set = ... # type: PrefixSet
|
||||
...
|
||||
|
||||
def cut(self, text: Text) -> Iterator[Text]: ...
|
||||
|
||||
def train(self, words: Iterator[Text]) -> None: ...
|
||||
|
||||
|
||||
class PrefixSet(object):
|
||||
def __init__(self) -> None:
|
||||
self._set = ... # type: Set[Text]
|
||||
...
|
||||
|
||||
def train(self, word_s: Iterator[Text]) -> None: ...
|
||||
|
||||
def __contains__(self, key: Text) -> bool: ...
|
||||
|
||||
|
||||
p_set = ... # type: PrefixSet
|
||||
seg = ... # type: Seg
|
||||
|
||||
|
||||
def retrain(seg_instance: Seg) -> None: ...
|
||||
@@ -0,0 +1,68 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from pypinyin.compat import text_type, bytes_type
|
||||
from pypinyin.constants import RE_HANS, PHRASES_DICT
|
||||
from pypinyin.seg import mmseg
|
||||
|
||||
|
||||
def seg(hans):
|
||||
hans = simple_seg(hans)
|
||||
ret = []
|
||||
for x in hans:
|
||||
if not RE_HANS.match(x): # 没有拼音的字符,不再参与二次分词
|
||||
ret.append(x)
|
||||
elif PHRASES_DICT:
|
||||
ret.extend(list(mmseg.seg.cut(x)))
|
||||
else: # 禁用了词语库,不分词
|
||||
ret.append(x)
|
||||
return ret
|
||||
|
||||
|
||||
def simple_seg(hans):
|
||||
"""将传入的字符串按是否是汉字来分割"""
|
||||
assert not isinstance(hans, bytes_type), \
|
||||
'must be unicode string or [unicode, ...] list'
|
||||
|
||||
if isinstance(hans, text_type):
|
||||
return _seg(hans)
|
||||
else:
|
||||
hans = list(hans)
|
||||
if len(hans) == 1:
|
||||
return simple_seg(hans[0])
|
||||
return list(chain(*[simple_seg(x) for x in hans]))
|
||||
|
||||
|
||||
def _seg(chars):
|
||||
"""按是否是汉字进行分词"""
|
||||
s = '' # 保存一个词
|
||||
ret = [] # 分词结果
|
||||
flag = 0 # 上一个字符是什么? 0: 汉字, 1: 不是汉字
|
||||
|
||||
for n, c in enumerate(chars):
|
||||
if RE_HANS.match(c): # 汉字, 确定 flag 的初始值
|
||||
if n == 0: # 第一个字符
|
||||
flag = 0
|
||||
|
||||
if flag == 0:
|
||||
s += c
|
||||
else: # 上一个字符不是汉字, 分词
|
||||
ret.append(s)
|
||||
flag = 0
|
||||
s = c
|
||||
|
||||
else: # 不是汉字
|
||||
if n == 0: # 第一个字符, 确定 flag 的初始值
|
||||
flag = 1
|
||||
|
||||
if flag == 1:
|
||||
s += c
|
||||
else: # 上一个字符是汉字, 分词
|
||||
ret.append(s)
|
||||
flag = 1
|
||||
s = c
|
||||
|
||||
ret.append(s) # 最后的词
|
||||
return ret
|
||||
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import List, Text
|
||||
|
||||
|
||||
def seg(hans: Text) -> List[Text]: ...
|
||||
|
||||
|
||||
def simple_seg(hans: Text) -> List[Text]: ...
|
||||
|
||||
|
||||
def _seg(chars: Text) -> List[Text]: ...
|
||||
Reference in New Issue
Block a user