mmseg.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # -*- coding: utf-8 -*-
  2. """最大正向匹配分词"""
  3. from pypinyin.constants import PHRASES_DICT
  4. class Seg(object):
  5. """正向最大匹配分词
  6. :type prefix_set: PrefixSet
  7. :param no_non_phrases: 是否严格按照词语分词,不允许把非词语的词当做词语进行分词
  8. :type no_non_phrases: bool
  9. """
  10. def __init__(self, prefix_set, no_non_phrases=False):
  11. self._prefix_set = prefix_set
  12. self._no_non_phrases = no_non_phrases
  13. def cut(self, text):
  14. """分词
  15. :param text: 待分词的文本
  16. :yield: 单个词语
  17. """
  18. remain = text
  19. while remain:
  20. matched = ''
  21. last_valid_word = ''
  22. last_valid_index = 0
  23. # 一次加一个字的匹配
  24. for index in range(len(remain)):
  25. word = remain[:index + 1]
  26. if word in self._prefix_set:
  27. matched = word
  28. # 检查当前匹配的词是否为有效词语
  29. if (not self._no_non_phrases) or word in PHRASES_DICT:
  30. last_valid_word = word
  31. last_valid_index = index + 1
  32. else:
  33. # 前缀匹配失败,需要处理之前的匹配结果
  34. if last_valid_word:
  35. # 有有效词语,输出最后一个有效词语
  36. yield last_valid_word
  37. remain = remain[last_valid_index:]
  38. else:
  39. # 没有有效词语
  40. if self._no_non_phrases:
  41. # 严格模式:输出第一个字符
  42. yield remain[0]
  43. remain = remain[1:]
  44. else:
  45. # 非严格模式:输出匹配到的前缀(如果有)或第一个字符
  46. if matched:
  47. yield matched
  48. remain = remain[len(matched):]
  49. else:
  50. yield remain[0]
  51. remain = remain[1:]
  52. break
  53. else: # 整个剩余文本都能匹配前缀
  54. if last_valid_word:
  55. # 有有效词语,输出最后一个有效词语
  56. yield last_valid_word
  57. remain = remain[last_valid_index:]
  58. else:
  59. # 没有有效词语,处理剩余文本
  60. if self._no_non_phrases and remain not in PHRASES_DICT:
  61. # 严格模式且不在词典中:拆分为单字符
  62. for x in remain:
  63. yield x
  64. else:
  65. # 非严格模式或在词典中:输出整个剩余文本
  66. yield remain
  67. break
  68. def train(self, words):
  69. """训练分词器
  70. :param words: 词语列表
  71. """
  72. self._prefix_set.train(words)
  73. class PrefixSet(object):
  74. def __init__(self):
  75. self._set = set()
  76. def train(self, word_s):
  77. """更新 prefix set
  78. :param word_s: 词语库列表
  79. :type word_s: iterable
  80. :return: None
  81. """
  82. for word in word_s:
  83. # 把词语的每个前缀更新到 prefix_set 中
  84. for index in range(len(word)):
  85. self._set.add(word[:index + 1])
  86. def __contains__(self, key):
  87. return key in self._set
  88. p_set = PrefixSet()
  89. p_set.train(PHRASES_DICT.keys())
  90. #: 基于内置词库的最大正向匹配分词器。使用:
  91. #:
  92. #: .. code-block:: python
  93. #:
  94. #: >>> from pypinyin.contrib.mmseg import seg
  95. #: >>> text = '你好,我是中国人,我爱我的祖国'
  96. #: >>> seg.cut(text)
  97. #: <generator object Seg.cut at 0x10b2df2b0>
  98. #: >>> list(seg.cut(text))
  99. #: ['你好', ',', '我', '是', '中国人', ',', '我', '爱',
  100. #: '我的', '祖', '国']
  101. #: >>> seg.train(['祖国', '我是'])
  102. #: >>> list(seg.cut(text))
  103. #: ['你好', ',', '我是', '中国人', ',', '我', '爱',
  104. #: '我的', '祖国']
  105. #: >>>
  106. seg = Seg(p_set, no_non_phrases=True)
  107. def retrain(seg_instance):
  108. """重新使用内置词典训练 seg_instance。
  109. 比如在增加自定义词语信息后需要调用这个模块重新训练分词器
  110. :type seg_instance: Seg
  111. """
  112. seg_instance.train(PHRASES_DICT.keys())