388 lines
15 KiB
Python
388 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
from copy import deepcopy
|
|
|
|
from pypinyin.compat import text_type, callable_check
|
|
from pypinyin.constants import (
|
|
PHRASES_DICT, PINYIN_DICT,
|
|
RE_HANS
|
|
)
|
|
from pypinyin.contrib.uv import V2UMixin
|
|
from pypinyin.contrib.neutral_tone import NeutralToneWith5Mixin
|
|
from pypinyin.contrib.tone_sandhi import ToneSandhiMixin
|
|
from pypinyin.utils import _remove_dup_and_empty
|
|
from pypinyin.style import auto_discover
|
|
from pypinyin.style import convert as convert_style
|
|
|
|
auto_discover()
|
|
|
|
|
|
class Converter(object):
|
|
|
|
def convert(self, words, style, heteronym, errors, strict, **kwargs):
|
|
# TODO: use ``abc`` module
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
|
|
class DefaultConverter(Converter):
|
|
def __init__(self, **kwargs):
|
|
pass
|
|
|
|
def convert(self, words, style, heteronym, errors, strict, **kwargs):
|
|
"""根据参数把汉字转成相应风格的拼音结果。
|
|
|
|
:param words: 汉字字符串
|
|
:type words: unicode
|
|
:param style: 拼音风格
|
|
:param heteronym: 是否启用多音字
|
|
:type heteronym: bool
|
|
:param errors: 如果处理没有拼音的字符
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:type strict: bool
|
|
:return: 按风格转换后的拼音结果
|
|
:rtype: list
|
|
|
|
"""
|
|
pys = []
|
|
# 初步过滤没有拼音的字符
|
|
if RE_HANS.match(words):
|
|
pys = self._phrase_pinyin(words, style=style, heteronym=heteronym,
|
|
errors=errors, strict=strict)
|
|
post_data = self.post_pinyin(words, heteronym, pys)
|
|
if post_data is not None:
|
|
pys = post_data
|
|
|
|
pys = self.convert_styles(
|
|
pys, words, style, heteronym, errors, strict)
|
|
|
|
else:
|
|
py = self.handle_nopinyin(words, style=style, errors=errors,
|
|
heteronym=heteronym, strict=strict)
|
|
if py:
|
|
pys.extend(py)
|
|
|
|
return _remove_dup_and_empty(pys)
|
|
|
|
def pre_convert_style(self, han, orig_pinyin, style, strict, **kwargs):
|
|
"""在把原始带声调的拼音按拼音风格转换前会调用 ``pre_convert_style`` 方法。
|
|
|
|
如果返回值不为 ``None`` 会使用返回的结果代替 ``orig_pinyin``
|
|
来进行后面的风格转换。
|
|
|
|
:param han: 要处理的汉字
|
|
:param orig_pinyin: 汉字对应的原始带声调拼音
|
|
:param style: 要转换的拼音风格
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: ``None`` 或代替 ``orig_pinyin`` 参与拼音风格转换的拼音字符串。
|
|
|
|
"""
|
|
pass
|
|
|
|
def convert_style(self, han, orig_pinyin, style, strict, **kwargs):
|
|
"""按 ``style`` 的值对 ``orig_pinyin`` 进行处理,返回处理后的拼音
|
|
|
|
转换风格前会调用 ``pre_convert_style`` 方法,
|
|
转换后会调用 ``post_convert_style`` 方法。
|
|
|
|
:param han: 要处理的单个汉字
|
|
:param orig_pinyin: 汉字对应的原始带声调拼音
|
|
:param style: 拼音风格
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: 按拼音风格转换处理后的拼音
|
|
|
|
"""
|
|
pre_data = self.pre_convert_style(
|
|
han, orig_pinyin, style=style, strict=strict)
|
|
if pre_data is not None:
|
|
pinyin = pre_data
|
|
else:
|
|
pinyin = orig_pinyin
|
|
|
|
converted_pinyin = self._convert_style(
|
|
han, pinyin, style=style, strict=strict, default=pinyin)
|
|
|
|
post_data = self.post_convert_style(
|
|
han, pinyin, converted_pinyin, style=style, strict=strict)
|
|
if post_data is None:
|
|
post_data = converted_pinyin
|
|
|
|
return post_data
|
|
|
|
def post_convert_style(self, han, orig_pinyin, converted_pinyin,
|
|
style, strict, **kwargs):
|
|
"""在把原始带声调的拼音按拼音风格转换前会调用 ``pre_convert_style`` 方法。
|
|
|
|
如果返回值不为 ``None`` 会使用返回的结果代替 ``converted_pinyin``
|
|
作为拼音风格转换后的最终拼音结果。
|
|
|
|
:param han: 要处理的汉字
|
|
:param orig_pinyin: 汉字对应的原始带声调拼音
|
|
:param converted_pinyin: 按拼音风格转换处理后的拼音
|
|
:param style: 要转换的拼音风格
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: ``None`` 或代替 ``converted_pinyin`` 作为拼音风格转换后的拼音结果。
|
|
|
|
"""
|
|
pass
|
|
|
|
def pre_handle_nopinyin(self, chars, style, heteronym, errors,
|
|
strict, **kwargs):
|
|
"""处理没有拼音的字符串前会调用 ``pre_handle_nopinyin`` 方法。
|
|
|
|
如果返回值不为 ``None`` 会使用返回的结果作为处理没有拼音字符串的结果,
|
|
不再使用内置方法进行处理。
|
|
|
|
:param chars: 待处理的没有拼音的字符串
|
|
:param errors: 如何处理
|
|
:param heteronym: 是否需要处理多音字
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: ``None`` 或代替 ``chars`` 参与拼音风格转换的拼音字符串
|
|
或拼音结果 list。
|
|
|
|
"""
|
|
pass
|
|
|
|
def handle_nopinyin(self, chars, style, heteronym, errors,
|
|
strict, **kwargs):
|
|
"""处理没有拼音的字符串。
|
|
|
|
处理前会调用 ``pre_handle_nopinyin`` 方法,
|
|
处理后会调用 ``post_handle_nopinyin`` 方法。
|
|
|
|
:param chars: 待处理的没有拼音的字符串
|
|
:param style: 拼音风格
|
|
:param errors: 如何处理
|
|
:param heteronym: 是否需要处理多音字
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:return: 处理后的拼音结果,如果为 ``None`` 或空 list 表示忽略这个字符串.
|
|
:rtype: list
|
|
"""
|
|
pre_data = self.pre_handle_nopinyin(
|
|
chars, style, errors=errors, heteronym=heteronym, strict=strict)
|
|
|
|
if pre_data is not None:
|
|
py = pre_data
|
|
else:
|
|
pre_data = chars
|
|
py = self._convert_nopinyin_chars(
|
|
pre_data, style, errors=errors,
|
|
heteronym=heteronym, strict=strict)
|
|
|
|
post_data = self.post_handle_nopinyin(
|
|
chars, style, errors=errors, heteronym=heteronym, strict=strict,
|
|
pinyin=py)
|
|
if post_data is not None:
|
|
py = post_data
|
|
|
|
if not py:
|
|
return []
|
|
if isinstance(py, list):
|
|
# 包含多音字信息
|
|
if isinstance(py[0], list):
|
|
if heteronym:
|
|
return py
|
|
# [[a, b], [c, d]]
|
|
# [[a], [c]]
|
|
return [[x[0]] for x in py]
|
|
|
|
return [[i] for i in py]
|
|
else:
|
|
return [[py]]
|
|
|
|
def post_handle_nopinyin(self, chars, style, heteronym,
|
|
errors, strict,
|
|
pinyin, **kwargs):
|
|
"""处理完没有拼音的字符串后会调用 ``post_handle_nopinyin`` 方法。
|
|
|
|
如果返回值不为 ``None`` 会使用返回的结果作为处理没有拼音的字符串的结果。
|
|
|
|
:param chars: 待处理的没有拼音的字符串
|
|
:param errors: 如何处理
|
|
:param heteronym: 是否需要处理多音字
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:param pinyin: 处理后的拼音信息,值为空 list 或包含拼音信息的 list
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: ``None`` 或代替 ``pinyin`` 做为处理结果。
|
|
|
|
"""
|
|
pass
|
|
|
|
def post_pinyin(self, han, heteronym, pinyin, **kwargs):
|
|
"""找到汉字对应的拼音后,会调用 ``post_pinyin`` 方法。
|
|
|
|
如果返回值不为 ``None`` 会使用返回的结果作为 han 的拼音数据。
|
|
|
|
:param han: 单个汉字或者词语
|
|
:param heteronym: 是否需要处理多音字
|
|
:param pinyin: 单个汉字的拼音数据或词语的拼音数据 list
|
|
:type pinyin: list
|
|
:param kwargs: 其他关键字参数,暂时无用,用于以后扩展新的参数。
|
|
:return: ``None`` 或代替 ``pinyin`` 作为 han 的拼音 list。
|
|
|
|
"""
|
|
pass
|
|
|
|
def _phrase_pinyin(self, phrase, style, heteronym, errors, strict):
|
|
"""词语拼音转换.
|
|
|
|
:param phrase: 词语
|
|
:param errors: 指定如何处理没有拼音的字符
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:return: 拼音列表
|
|
:rtype: list
|
|
"""
|
|
pinyin_list = []
|
|
if phrase in PHRASES_DICT:
|
|
pinyin_list = deepcopy(PHRASES_DICT[phrase])
|
|
else:
|
|
for han in phrase:
|
|
py = self._single_pinyin(han, style, heteronym, errors, strict)
|
|
pinyin_list.extend(py)
|
|
|
|
return pinyin_list
|
|
|
|
def convert_styles(self, pinyin_list, phrase, style, heteronym, errors,
|
|
strict, **kwargs):
|
|
"""转换多个汉字的拼音结果的风格"""
|
|
for idx, item in enumerate(pinyin_list):
|
|
han = phrase[idx]
|
|
if heteronym:
|
|
pinyin_list[idx] = [
|
|
self.convert_style(
|
|
han, orig_pinyin=x, style=style, strict=strict)
|
|
for x in item
|
|
]
|
|
else:
|
|
orig_pinyin = item[0]
|
|
pinyin_list[idx] = [
|
|
self.convert_style(
|
|
han, orig_pinyin=orig_pinyin, style=style,
|
|
strict=strict)]
|
|
|
|
return pinyin_list
|
|
|
|
def _single_pinyin(self, han, style, heteronym, errors, strict):
|
|
"""单字拼音转换.
|
|
|
|
:param han: 单个汉字
|
|
:param errors: 指定如何处理没有拼音的字符,详情请参考
|
|
:py:func:`~pypinyin.pinyin`
|
|
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
|
|
是否严格遵照《汉语拼音方案》来处理声母和韵母,
|
|
详见 :ref:`strict`
|
|
:return: 返回拼音列表,多音字会有多个拼音项
|
|
:rtype: list
|
|
"""
|
|
num = ord(han)
|
|
# 处理没有拼音的字符
|
|
if num not in PINYIN_DICT:
|
|
return self.handle_nopinyin(
|
|
han, style=style, errors=errors,
|
|
heteronym=heteronym, strict=strict)
|
|
|
|
pys = PINYIN_DICT[num].split(',') # 字的拼音列表
|
|
return [pys]
|
|
|
|
def _convert_style(self, han, pinyin, style, strict, default,
|
|
**kwargs):
|
|
if not kwargs:
|
|
kwargs = {}
|
|
kwargs['han'] = han
|
|
|
|
return convert_style(pinyin, style, strict, default=default, **kwargs)
|
|
|
|
def _convert_nopinyin_chars(self, chars, style, heteronym, errors, strict):
|
|
"""转换没有拼音的字符。
|
|
|
|
"""
|
|
if callable_check(errors):
|
|
return errors(chars)
|
|
|
|
if errors == 'default':
|
|
return chars
|
|
elif errors == 'ignore':
|
|
return None
|
|
elif errors == 'replace':
|
|
if len(chars) > 1:
|
|
return ''.join(text_type('%x' % ord(x)) for x in chars)
|
|
else:
|
|
return text_type('%x' % ord(chars))
|
|
|
|
|
|
class _v2UConverter(V2UMixin, DefaultConverter):
|
|
pass
|
|
|
|
|
|
class _neutralToneWith5Converter(NeutralToneWith5Mixin, DefaultConverter):
|
|
pass
|
|
|
|
|
|
class _toneSandhiConverter(ToneSandhiMixin, DefaultConverter):
|
|
pass
|
|
|
|
|
|
class UltimateConverter(DefaultConverter):
|
|
def __init__(self, v_to_u=False, neutral_tone_with_five=False,
|
|
tone_sandhi=False, **kwargs):
|
|
super(UltimateConverter, self).__init__(**kwargs)
|
|
self._v_to_u = v_to_u
|
|
self._neutral_tone_with_five = neutral_tone_with_five
|
|
self._tone_sandhi = tone_sandhi
|
|
|
|
def post_convert_style(self, han, orig_pinyin, converted_pinyin,
|
|
style, strict, **kwargs):
|
|
post_data = super(UltimateConverter, self).post_convert_style(
|
|
han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
|
|
if post_data is not None:
|
|
converted_pinyin = post_data
|
|
|
|
if self._v_to_u:
|
|
post_data = _v2UConverter().post_convert_style(
|
|
han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
|
|
if post_data is not None:
|
|
converted_pinyin = post_data
|
|
|
|
if self._neutral_tone_with_five:
|
|
post_data = _neutralToneWith5Converter().post_convert_style(
|
|
han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
|
|
if post_data is not None:
|
|
converted_pinyin = post_data
|
|
|
|
return converted_pinyin
|
|
|
|
def post_pinyin(self, han, heteronym, pinyin, **kwargs):
|
|
post_data = super(UltimateConverter, self).post_pinyin(
|
|
han, heteronym, pinyin, **kwargs)
|
|
if post_data is not None:
|
|
pinyin = post_data
|
|
|
|
if self._tone_sandhi:
|
|
post_data = _toneSandhiConverter().post_pinyin(
|
|
han, heteronym, pinyin, **kwargs)
|
|
if post_data is not None:
|
|
pinyin = post_data
|
|
|
|
return pinyin
|
|
|
|
|
|
_mixConverter = UltimateConverter
|