converter.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from copy import deepcopy
  4. from pypinyin.compat import text_type, callable_check
  5. from pypinyin.constants import (
  6. PHRASES_DICT, PINYIN_DICT,
  7. RE_HANS
  8. )
  9. from pypinyin.contrib.uv import V2UMixin
  10. from pypinyin.contrib.neutral_tone import NeutralToneWith5Mixin
  11. from pypinyin.contrib.tone_sandhi import ToneSandhiMixin
  12. from pypinyin.exceptions import PinyinNotFoundException
  13. from pypinyin.utils import _remove_dup_and_empty
  14. from pypinyin.style import auto_discover
  15. from pypinyin.style import convert as convert_style
  16. auto_discover()
  17. class Converter(object):
  18. def convert(self, words, style, heteronym, errors, strict, **kwargs):
  19. # TODO: use ``abc`` module
  20. raise NotImplementedError # pragma: no cover
  21. class DefaultConverter(Converter):
  22. def __init__(self, **kwargs):
  23. pass
  24. def convert(self, words, style, heteronym, errors, strict, **kwargs):
  25. """根据参数把汉字转成相应风格的拼音结果。
  26. :param words: 汉字字符串
  27. :type words: unicode
  28. :param style: 拼音风格
  29. :param heteronym: 是否启用多音字
  30. :type heteronym: bool
  31. :param errors: 如何处理没有拼音的字符
  32. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  33. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  34. 详见 :ref:`strict`
  35. :type strict: bool
  36. :return: 按风格转换后的拼音结果
  37. :rtype: list
  38. """
  39. pys = []
  40. # 初步过滤没有拼音的字符
  41. if RE_HANS.match(words):
  42. pys = self._phrase_pinyin(words, style=style, heteronym=heteronym,
  43. errors=errors, strict=strict)
  44. post_data = self.post_pinyin(words, heteronym, pys)
  45. if post_data is not None:
  46. pys = post_data
  47. pys = self.convert_styles(
  48. pys, words, style, heteronym, errors, strict)
  49. else:
  50. py = self.handle_nopinyin(words, style=style, errors=errors,
  51. heteronym=heteronym, strict=strict)
  52. if py:
  53. pys.extend(py)
  54. return _remove_dup_and_empty(pys)
  55. def pre_convert_style(self, han, orig_pinyin, style, strict, **kwargs):
  56. """在把原始带声调的拼音按拼音风格转换前会调用 ``pre_convert_style`` 方法。
  57. 如果返回值不为 ``None`` 会使用返回的结果代替 ``orig_pinyin``
  58. 来进行后面的风格转换。
  59. :param han: 要处理的汉字
  60. :param orig_pinyin: 汉字对应的原始带声调拼音
  61. :param style: 要转换的拼音风格
  62. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  63. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  64. 详见 :ref:`strict`
  65. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  66. :return: ``None`` 或代替 ``orig_pinyin`` 参与拼音风格转换的拼音字符串。
  67. """
  68. pass
  69. def convert_style(self, han, orig_pinyin, style, strict, **kwargs):
  70. """按 ``style`` 的值对 ``orig_pinyin`` 进行处理,返回处理后的拼音
  71. 转换风格前会调用 ``pre_convert_style`` 方法,
  72. 转换后会调用 ``post_convert_style`` 方法。
  73. :param han: 要处理的单个汉字
  74. :param orig_pinyin: 汉字对应的原始带声调拼音
  75. :param style: 拼音风格
  76. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  77. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  78. 详见 :ref:`strict`
  79. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  80. :return: 按拼音风格转换处理后的拼音
  81. """
  82. pre_data = self.pre_convert_style(
  83. han, orig_pinyin, style=style, strict=strict)
  84. if pre_data is not None:
  85. pinyin = pre_data
  86. else:
  87. pinyin = orig_pinyin
  88. converted_pinyin = self._convert_style(
  89. han, pinyin, style=style, strict=strict, default=pinyin)
  90. post_data = self.post_convert_style(
  91. han, pinyin, converted_pinyin, style=style, strict=strict)
  92. if post_data is None:
  93. post_data = converted_pinyin
  94. return post_data
  95. def post_convert_style(self, han, orig_pinyin, converted_pinyin,
  96. style, strict, **kwargs):
  97. """在把原始带声调的拼音按拼音风格转换前会调用 ``pre_convert_style`` 方法。
  98. 如果返回值不为 ``None`` 会使用返回的结果代替 ``converted_pinyin``
  99. 作为拼音风格转换后的最终拼音结果。
  100. :param han: 要处理的汉字
  101. :param orig_pinyin: 汉字对应的原始带声调拼音
  102. :param converted_pinyin: 按拼音风格转换处理后的拼音
  103. :param style: 要转换的拼音风格
  104. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  105. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  106. 详见 :ref:`strict`
  107. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  108. :return: ``None`` 或代替 ``converted_pinyin`` 作为拼音风格转换后的拼音结果。
  109. """
  110. pass
  111. def pre_handle_nopinyin(self, chars, style, heteronym, errors,
  112. strict, **kwargs):
  113. """处理没有拼音的字符串前会调用 ``pre_handle_nopinyin`` 方法。
  114. 如果返回值不为 ``None`` 会使用返回的结果作为处理没有拼音字符串的结果,
  115. 不再使用内置方法进行处理。
  116. :param chars: 待处理的没有拼音的字符串
  117. :param errors: 如何处理
  118. :param heteronym: 是否需要处理多音字
  119. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  120. :return: ``None`` 或代替 ``chars`` 参与拼音风格转换的拼音字符串
  121. 或拼音结果 list。
  122. """
  123. pass
  124. def handle_nopinyin(self, chars, style, heteronym, errors,
  125. strict, **kwargs):
  126. """处理没有拼音的字符串。
  127. 处理前会调用 ``pre_handle_nopinyin`` 方法,
  128. 处理后会调用 ``post_handle_nopinyin`` 方法。
  129. :param chars: 待处理的没有拼音的字符串
  130. :param style: 拼音风格
  131. :param errors: 如何处理
  132. :param heteronym: 是否需要处理多音字
  133. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  134. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  135. 详见 :ref:`strict`
  136. :return: 处理后的拼音结果,如果为 ``None`` 或空 list 表示忽略这个字符串.
  137. :rtype: list
  138. """
  139. pre_data = self.pre_handle_nopinyin(
  140. chars, style, errors=errors, heteronym=heteronym, strict=strict)
  141. if pre_data is not None:
  142. py = pre_data
  143. else:
  144. pre_data = chars
  145. py = self._convert_nopinyin_chars(
  146. pre_data, style, errors=errors,
  147. heteronym=heteronym, strict=strict)
  148. post_data = self.post_handle_nopinyin(
  149. chars, style, errors=errors, heteronym=heteronym, strict=strict,
  150. pinyin=py)
  151. if post_data is not None:
  152. py = post_data
  153. if not py:
  154. return []
  155. if isinstance(py, list):
  156. # 包含多音字信息
  157. if isinstance(py[0], list):
  158. if heteronym:
  159. return py
  160. # [[a, b], [c, d]]
  161. # [[a], [c]]
  162. return [[x[0]] for x in py]
  163. return [[i] for i in py]
  164. else:
  165. return [[py]]
  166. def post_handle_nopinyin(self, chars, style, heteronym,
  167. errors, strict,
  168. pinyin, **kwargs):
  169. """处理完没有拼音的字符串后会调用 ``post_handle_nopinyin`` 方法。
  170. 如果返回值不为 ``None`` 会使用返回的结果作为处理没有拼音的字符串的结果。
  171. :param chars: 待处理的没有拼音的字符串
  172. :param errors: 如何处理
  173. :param heteronym: 是否需要处理多音字
  174. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  175. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  176. 详见 :ref:`strict`
  177. :param pinyin: 处理后的拼音信息,值为空 list 或包含拼音信息的 list
  178. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  179. :return: ``None`` 或代替 ``pinyin`` 做为处理结果。
  180. """
  181. pass
  182. def post_pinyin(self, han, heteronym, pinyin, **kwargs):
  183. """找到汉字对应的拼音后,会调用 ``post_pinyin`` 方法。
  184. 如果返回值不为 ``None`` 会使用返回的结果作为 han 的拼音数据。
  185. :param han: 单个汉字或者词语
  186. :param heteronym: 是否需要处理多音字
  187. :param pinyin: 单个汉字的拼音数据或词语的拼音数据 list
  188. :type pinyin: list
  189. :param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
  190. :return: ``None`` 或代替 ``pinyin`` 作为 han 的拼音 list。
  191. """
  192. pass
  193. def _phrase_pinyin(self, phrase, style, heteronym, errors, strict):
  194. """词语拼音转换.
  195. :param phrase: 词语
  196. :param errors: 指定如何处理没有拼音的字符
  197. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  198. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  199. 详见 :ref:`strict`
  200. :return: 拼音列表
  201. :rtype: list
  202. """
  203. pinyin_list = []
  204. if phrase in PHRASES_DICT:
  205. pinyin_list = deepcopy(PHRASES_DICT[phrase])
  206. else:
  207. for han in phrase:
  208. py = self._single_pinyin(han, style, heteronym, errors, strict)
  209. pinyin_list.extend(py)
  210. return pinyin_list
  211. def convert_styles(self, pinyin_list, phrase, style, heteronym, errors,
  212. strict, **kwargs):
  213. """转换多个汉字的拼音结果的风格"""
  214. for idx, item in enumerate(pinyin_list):
  215. han = phrase[idx]
  216. if heteronym:
  217. pinyin_list[idx] = [
  218. self.convert_style(
  219. han, orig_pinyin=x, style=style, strict=strict)
  220. for x in item
  221. ]
  222. else:
  223. orig_pinyin = item[0]
  224. pinyin_list[idx] = [
  225. self.convert_style(
  226. han, orig_pinyin=orig_pinyin, style=style,
  227. strict=strict)]
  228. return pinyin_list
  229. def _single_pinyin(self, han, style, heteronym, errors, strict):
  230. """单字拼音转换.
  231. :param han: 单个汉字
  232. :param errors: 指定如何处理没有拼音的字符,详情请参考
  233. :py:func:`~pypinyin.pinyin`
  234. :param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
  235. 是否严格遵照《汉语拼音方案》来处理声母和韵母,
  236. 详见 :ref:`strict`
  237. :return: 返回拼音列表,多音字会有多个拼音项
  238. :rtype: list
  239. """
  240. num = ord(han)
  241. # 处理没有拼音的字符
  242. if num not in PINYIN_DICT:
  243. return self.handle_nopinyin(
  244. han, style=style, errors=errors,
  245. heteronym=heteronym, strict=strict)
  246. pys = PINYIN_DICT[num].split(',') # 字的拼音列表
  247. return [pys]
  248. def _convert_style(self, han, pinyin, style, strict, default,
  249. **kwargs):
  250. if not kwargs:
  251. kwargs = {}
  252. kwargs['han'] = han
  253. return convert_style(pinyin, style, strict, default=default, **kwargs)
  254. def _convert_nopinyin_chars(self, chars, style, heteronym, errors, strict):
  255. """转换没有拼音的字符。
  256. """
  257. if callable_check(errors):
  258. return errors(chars)
  259. if errors == 'default':
  260. return chars
  261. elif errors == 'ignore':
  262. return None
  263. elif errors == 'exception':
  264. raise PinyinNotFoundException(chars)
  265. elif errors == 'replace':
  266. if len(chars) > 1:
  267. return ''.join(text_type('%x' % ord(x)) for x in chars)
  268. else:
  269. return text_type('%x' % ord(chars))
  270. class _v2UConverter(V2UMixin, DefaultConverter):
  271. pass
  272. class _neutralToneWith5Converter(NeutralToneWith5Mixin, DefaultConverter):
  273. pass
  274. class _toneSandhiConverter(ToneSandhiMixin, DefaultConverter):
  275. pass
  276. class UltimateConverter(DefaultConverter):
  277. def __init__(self, v_to_u=False, neutral_tone_with_five=False,
  278. tone_sandhi=False, **kwargs):
  279. super(UltimateConverter, self).__init__(**kwargs)
  280. self._v_to_u = v_to_u
  281. self._neutral_tone_with_five = neutral_tone_with_five
  282. self._tone_sandhi = tone_sandhi
  283. def post_convert_style(self, han, orig_pinyin, converted_pinyin,
  284. style, strict, **kwargs):
  285. post_data = super(UltimateConverter, self).post_convert_style(
  286. han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
  287. if post_data is not None:
  288. converted_pinyin = post_data
  289. if self._v_to_u:
  290. post_data = _v2UConverter().post_convert_style(
  291. han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
  292. if post_data is not None:
  293. converted_pinyin = post_data
  294. if self._neutral_tone_with_five:
  295. post_data = _neutralToneWith5Converter().post_convert_style(
  296. han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
  297. if post_data is not None:
  298. converted_pinyin = post_data
  299. return converted_pinyin
  300. def post_pinyin(self, han, heteronym, pinyin, **kwargs):
  301. post_data = super(UltimateConverter, self).post_pinyin(
  302. han, heteronym, pinyin, **kwargs)
  303. if post_data is not None:
  304. pinyin = post_data
  305. if self._tone_sandhi:
  306. post_data = _toneSandhiConverter().post_pinyin(
  307. han, heteronym, pinyin, **kwargs)
  308. if post_data is not None:
  309. pinyin = post_data
  310. return pinyin
  311. _mixConverter = UltimateConverter