Restore 0.1.5 version from stash

This commit is contained in:
liaibo
2025-12-08 19:56:24 +08:00
parent de189e938d
commit 8db3f4e32d
8578 changed files with 2703426 additions and 217 deletions
@@ -0,0 +1,56 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""汉字拼音转换工具."""
from __future__ import unicode_literals
from pypinyin.compat import PY2
from pypinyin.constants import ( # noqa
Style,
STYLE_NORMAL, NORMAL,
STYLE_TONE, TONE,
STYLE_TONE2, TONE2,
STYLE_TONE3, TONE3,
STYLE_INITIALS, INITIALS,
STYLE_FIRST_LETTER, FIRST_LETTER,
STYLE_FINALS, FINALS,
STYLE_FINALS_TONE, FINALS_TONE,
STYLE_FINALS_TONE2, FINALS_TONE2,
STYLE_FINALS_TONE3, FINALS_TONE3,
STYLE_BOPOMOFO, BOPOMOFO,
STYLE_BOPOMOFO_FIRST, BOPOMOFO_FIRST,
STYLE_CYRILLIC, CYRILLIC,
STYLE_CYRILLIC_FIRST, CYRILLIC_FIRST
)
from pypinyin.core import ( # noqa
pinyin, lazy_pinyin, slug, load_single_dict, load_phrases_dict
)
__title__ = 'pypinyin'
__version__ = '0.55.0'
__author__ = 'mozillazg, 闲耘'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2016 mozillazg, 闲耘'
__all__ = [
'pinyin', 'lazy_pinyin', 'slug',
'load_single_dict', 'load_phrases_dict',
'Style',
'STYLE_NORMAL', 'NORMAL',
'STYLE_TONE', 'TONE',
'STYLE_TONE2', 'TONE2',
'STYLE_TONE3', 'TONE3',
'STYLE_INITIALS', 'INITIALS',
'STYLE_FINALS', 'FINALS',
'STYLE_FINALS_TONE', 'FINALS_TONE',
'STYLE_FINALS_TONE2', 'FINALS_TONE2',
'STYLE_FINALS_TONE3', 'FINALS_TONE3',
'STYLE_FIRST_LETTER', 'FIRST_LETTER',
'STYLE_BOPOMOFO', 'BOPOMOFO',
'STYLE_BOPOMOFO_FIRST', 'BOPOMOFO_FIRST',
'STYLE_CYRILLIC', 'CYRILLIC',
'STYLE_CYRILLIC_FIRST', 'CYRILLIC_FIRST'
]
if PY2:
# fix "TypeError: Item in ``from list'' not a string" on Python 2
__all__ = [x.encode('utf-8') for x in __all__]
@@ -0,0 +1,50 @@
from typing import Any
from . import compat
from . import constants
from . import core
__title__ = ... # type: Any
__version__ = ... # type: Any
__author__ = ... # type: Any
__license__ = ... # type: Any
__copyright__ = ... # type: Any
__all__ = ... # type: Any
PY2 = compat.PY2
Style = constants.Style
STYLE_NORMAL = constants.STYLE_NORMAL
NORMAL = constants.NORMAL
STYLE_TONE = constants.STYLE_TONE
TONE = constants.TONE
STYLE_TONE2 = constants.STYLE_TONE2
TONE2 = constants.TONE2
STYLE_TONE3 = constants.STYLE_TONE3
TONE3 = constants.TONE3
STYLE_INITIALS = constants.STYLE_INITIALS
INITIALS = constants.INITIALS
STYLE_FIRST_LETTER = constants.STYLE_FIRST_LETTER
FIRST_LETTER = constants.FIRST_LETTER
STYLE_FINALS = constants.STYLE_FINALS
FINALS = constants.FINALS
STYLE_FINALS_TONE = constants.STYLE_FINALS_TONE
FINALS_TONE = constants.FINALS_TONE
STYLE_FINALS_TONE2 = constants.STYLE_FINALS_TONE2
FINALS_TONE2 = constants.FINALS_TONE2
STYLE_FINALS_TONE3 = constants.STYLE_FINALS_TONE3
FINALS_TONE3 = constants.FINALS_TONE3
STYLE_BOPOMOFO = constants.STYLE_BOPOMOFO
BOPOMOFO = constants.BOPOMOFO
STYLE_BOPOMOFO_FIRST = constants.STYLE_BOPOMOFO_FIRST
BOPOMOFO_FIRST = constants.BOPOMOFO_FIRST
STYLE_CYRILLIC = constants.STYLE_CYRILLIC
CYRILLIC = constants.CYRILLIC
STYLE_CYRILLIC_FIRST = constants.STYLE_CYRILLIC_FIRST
CYRILLIC_FIRST = constants.CYRILLIC_FIRST
pinyin = core.pinyin
lazy_pinyin = core.lazy_pinyin
slug = core.slug
load_single_dict = core.load_single_dict
load_phrases_dict = core.load_phrases_dict
@@ -0,0 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pypinyin.runner import main
if __name__ == '__main__':
main()
@@ -0,0 +1,7 @@
# reference: https://github.com/pyinstaller/hooksample/blob/master/src/pyi_hooksample/__pyinstaller/__init__.py # noqa
import os
def get_hook_dirs():
return [os.path.dirname(__file__)]
@@ -0,0 +1,4 @@
from typing import Text, List
def get_hook_dirs() -> List[Text]: ...
@@ -0,0 +1,6 @@
# reference: https://github.com/pyinstaller/hooksample/blob/master/src/pyi_hooksample/__pyinstaller/hook-pyi_hooksample.py # noqa
from PyInstaller.utils.hooks import collect_data_files # type: ignore
datas = collect_data_files(
'pypinyin', excludes=['__pyinstaller'])
@@ -0,0 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
SUPPORT_UCS4 = len('\U00020000') == 1
PY2 = sys.version_info < (3, 0)
subversion = getattr(sys, 'subversion', [''])
# 这些 Python 实现虽然是 Python 2 但字符串的行为跟 Python 3 是一样的
if subversion[0] in (
'IronPython',
):
PY2 = False # pragma: no cover
if not PY2:
text_type = str
bytes_type = bytes
else:
text_type = unicode # noqa
bytes_type = str
try:
callable_check = callable # noqa
except NameError:
def callable_check(obj):
return hasattr(obj, '__call__')
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from typing import Any, List, Text, ByteString
SUPPORT_UCS4 = ... # type: bool
PY2 = ... # type: bool
subversion = ... # type: List[Text]
text_type = ... # type: Text
bytes_type = ... # type: ByteString
def callable_check(obj: Any) -> bool: ...
@@ -0,0 +1,125 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import re
from enum import IntEnum, unique
from pypinyin import pinyin_dict
from pypinyin.compat import SUPPORT_UCS4
# 词语拼音库
if os.environ.get('PYPINYIN_NO_PHRASES'):
PHRASES_DICT = {}
else:
from pypinyin import phrases_dict
PHRASES_DICT = phrases_dict.phrases_dict
# 单字拼音库
PINYIN_DICT = pinyin_dict.pinyin_dict
# 利用环境变量控制不做copy操作(无自定义拼音库的情况), 以减少内存使用
if not os.environ.get('PYPINYIN_NO_DICT_COPY'):
PINYIN_DICT = PINYIN_DICT.copy()
PHRASES_DICT = PHRASES_DICT.copy()
# 匹配使用数字标识声调的字符的正则表达式
RE_TONE2 = re.compile(r'([aeoiuvnm])([1-4])$')
# 有拼音的汉字
if SUPPORT_UCS4:
RE_HANS = re.compile(
r'^(?:['
r'\u3007' #
r'\ue815-\ue864'
r'\ufa18'
r'\u3400-\u4dbf' # CJK扩展A:[3400-4DBF]
r'\u4e00-\u9fff' # CJK基本:[4E00-9FFF]
r'\uf900-\ufaff' # CJK兼容:[F900-FAFF]
r'\U00020000-\U0002A6DF' # CJK扩展B:[20000-2A6DF]
r'\U0002A703-\U0002B73F' # CJK扩展C:[2A700-2B73F]
r'\U0002B740-\U0002B81D' # CJK扩展D:[2B740-2B81D]
r'\U0002B825-\U0002BF6E' # CJK扩展E:
r'\U0002C029-\U0002CE93' # CJK扩展F:
r'\U0002D016'
r'\U0002D11B-\U0002EBD9'
r'\U0002F80A-\U0002FA1F' # CJK兼容扩展:[2F800-2FA1F]
r'\U00030000-\U0003134A' # CJK扩展G:
r'\U000300F7-\U00031288'
r'\U00030EDD'
r'\U00030EDE'
r'\U00031350-\U00032389'
r'])+$'
)
else:
RE_HANS = re.compile(
r'^(?:['
r'\u3007' #
r'\ue815-\ue864'
r'\ufa18'
r'\u3400-\u4dbf' # CJK扩展A:[3400-4DBF]
r'\u4e00-\u9fff' # CJK基本:[4E00-9FFF]
r'\uf900-\ufaff' # CJK兼容:[F900-FAFF]
r'])+$'
)
@unique
class Style(IntEnum):
"""拼音风格"""
#: 普通风格,不带声调。如: 中国 -> ``zhong guo``
NORMAL = 0
#: 标准声调风格,拼音声调在韵母第一个字母上(默认风格)。如: 中国 -> ``zhōng guó``
TONE = 1
#: 声调风格2,即拼音声调在各个韵母之后,用数字 [1-4] 进行表示。如: 中国 -> ``zho1ng guo2``
TONE2 = 2
#: 声调风格3,即拼音声调在各个拼音之后,用数字 [1-4] 进行表示。如: 中国 -> ``zhong1 guo2``
TONE3 = 8
#: 声母风格,只返回各个拼音的声母部分(注:有的拼音没有声母,详见 `#27`_)。如: 中国 -> ``zh g``
INITIALS = 3
#: 首字母风格,只返回拼音的首字母部分。如: 中国 -> ``z g``
FIRST_LETTER = 4
#: 韵母风格,只返回各个拼音的韵母部分,不带声调。如: 中国 -> ``ong uo``
FINALS = 5
#: 标准韵母风格,带声调,声调在韵母第一个字母上。如:中国 -> ``ōng uó``
FINALS_TONE = 6
#: 韵母风格2,带声调,声调在各个韵母之后,用数字 [1-4] 进行表示。如: 中国 -> ``o1ng uo2``
FINALS_TONE2 = 7
#: 韵母风格3,带声调,声调在各个拼音之后,用数字 [1-4] 进行表示。如: 中国 -> ``ong1 uo2``
FINALS_TONE3 = 9
#: 注音风格,带声调,阴平(第一声)不标。如: 中国 -> ``ㄓㄨㄥ ㄍㄨㄛˊ``
BOPOMOFO = 10
#: 注音风格,仅首字母。如: 中国 -> ``ㄓ ㄍ``
BOPOMOFO_FIRST = 11
#: 汉语拼音与俄语字母对照风格,声调在各个拼音之后,用数字 [1-4] 进行表示。如: 中国 -> ``чжун1 го2``
CYRILLIC = 12
#: 汉语拼音与俄语字母对照风格,仅首字母。如: 中国 -> ``ч г``
CYRILLIC_FIRST = 13
#: 威妥玛拼音/韦氏拼音/威式拼音风格,无声调
WADEGILES = 14
#: 国语罗马字风格。如:中国 -> ``jong gwo``
GWOYEU = 15
#: 中国内地盲文风格,无声调。如:中国 -> ``⠌⠲ ⠛⠕``
BRAILLE_MAINLAND = 16
#: 中国内地盲文风格,带声调。如:中国 -> ``⠌⠲⠁ ⠛⠕⠂``
BRAILLE_MAINLAND_TONE = 17
NORMAL = STYLE_NORMAL = Style.NORMAL
TONE = STYLE_TONE = Style.TONE
TONE2 = STYLE_TONE2 = Style.TONE2
TONE3 = STYLE_TONE3 = Style.TONE3
INITIALS = STYLE_INITIALS = Style.INITIALS
FIRST_LETTER = STYLE_FIRST_LETTER = Style.FIRST_LETTER
FINALS = STYLE_FINALS = Style.FINALS
FINALS_TONE = STYLE_FINALS_TONE = Style.FINALS_TONE
FINALS_TONE2 = STYLE_FINALS_TONE2 = Style.FINALS_TONE2
FINALS_TONE3 = STYLE_FINALS_TONE3 = Style.FINALS_TONE3
BOPOMOFO = STYLE_BOPOMOFO = Style.BOPOMOFO
BOPOMOFO_FIRST = STYLE_BOPOMOFO_FIRST = Style.BOPOMOFO_FIRST
CYRILLIC = STYLE_CYRILLIC = Style.CYRILLIC
CYRILLIC_FIRST = STYLE_CYRILLIC_FIRST = Style.CYRILLIC_FIRST
@@ -0,0 +1,78 @@
from enum import IntEnum, unique
from typing import Dict, List, Any, Text
PHRASES_DICT = ... # type: Dict[Text, List[List[Text]]]
PINYIN_DICT = ... # type: Dict[int, Text]
RE_TONE2 = ... # type: Any
RE_HANS = ... # type: Any
@unique
class Style(IntEnum):
NORMAL = ...
TONE = ...
TONE2 = ...
TONE3 = ...
INITIALS = ...
FIRST_LETTER = ...
FINALS = ...
FINALS_TONE = ...
FINALS_TONE2 = ...
FINALS_TONE3 = ...
BOPOMOFO = ...
BOPOMOFO_FIRST = ...
CYRILLIC = ...
CYRILLIC_FIRST = ...
WADEGILES = ...
GWOYEU = ...
BRAILLE_MAINLAND_TONE = ...
BRAILLE_MAINLAND = ...
NORMAL = ... # type: Style
STYLE_NORMAL = ... # type: Style
TONE = ... # type: Style
STYLE_TONE = ... # type: Style
TONE2 = ... # type: Style
STYLE_TONE2 = ... # type: Style
TONE3 = ... # type: Style
STYLE_TONE3 = ... # type: Style
INITIALS = ... # type: Style
STYLE_INITIALS = ... # type: Style
FIRST_LETTER = ... # type: Style
STYLE_FIRST_LETTER = ... # type: Style
FINALS = ... # type: Style
STYLE_FINALS = ... # type: Style
FINALS_TONE = ... # type: Style
STYLE_FINALS_TONE = ... # type: Style
FINALS_TONE2 = ... # type: Style
STYLE_FINALS_TONE2 = ... # type: Style
FINALS_TONE3 = ... # type: Style
STYLE_FINALS_TONE3 = ... # type: Style
BOPOMOFO = ... # type: Style
STYLE_BOPOMOFO = ... # type: Style
BOPOMOFO_FIRST = ... # type: Style
STYLE_BOPOMOFO_FIRST = ... # type: Style
CYRILLIC = ... # type: Style
STYLE_CYRILLIC = ... # type: Style
CYRILLIC_FIRST = ... # type: Style
STYLE_CYRILLIC_FIRST = ... # type: Style
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# 向后兼容
from pypinyin.style._tone_rule import right_mark_index # noqa
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from typing import Text, Optional
def right_mark_index(pinyin_no_number: Text) -> Optional[int]: ...
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
"""最大正向匹配分词"""
# 用于向后兼容,TODO: 废弃
from pypinyin.seg.mmseg import Seg, PrefixSet, seg, retrain, p_set # noqa
@@ -0,0 +1,29 @@
from typing import Iterator
from typing import Text
class Seg(object):
"""最大正向匹配分词
:type prefix_set: PrefixSet
"""
def __init__(self, prefix_set: PrefixSet) -> None: ...
def cut(self, text: Text) -> Iterator[Text]: ...
def train(self, words: Iterator[Text]) -> None: ...
class PrefixSet(object):
def __init__(self) -> None: ...
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,73 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from pypinyin import Style
from pypinyin.contrib._tone_rule import right_mark_index
_re_number = re.compile(r'\d')
class NeutralToneWith5Mixin(object):
"""声调使用数字表示的相关拼音风格下的结果使用 5 标识轻声。
使用方法::
from pypinyin import lazy_pinyin, Style
from pypinyin.contrib.neutral_tone import NeutralToneWith5Mixin
from pypinyin.converter import DefaultConverter
from pypinyin.core import Pinyin
# 原来的结果中不会标识轻声
print(lazy_pinyin('好了', style=Style.TONE2))
# 输出: ['ha3o', 'le']
class MyConverter(NeutralToneWith5Mixin, DefaultConverter):
pass
my_pinyin = Pinyin(MyConverter())
pinyin = my_pinyin.pinyin
lazy_pinyin = my_pinyin.lazy_pinyin
# 新的结果中使用 ``5`` 标识轻声
print(lazy_pinyin('好了', style=Style.TONE2))
# 输出: ['ha3o', 'le5']
print(pinyin('好了', style=Style.TONE2))
# 输出:[['ha3o'], ['le5']]
"""
NUMBER_TONE = (Style.TONE2, Style.TONE3, Style.FINALS_TONE2,
Style.FINALS_TONE3)
NUMBER_AT_END = (Style.TONE3, Style.FINALS_TONE3)
def post_convert_style(self, han, orig_pinyin, converted_pinyin,
style, strict, **kwargs):
pre_data = super(NeutralToneWith5Mixin, self).post_convert_style(
han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
if style not in self.NUMBER_TONE:
return pre_data
if pre_data is not None:
converted_pinyin = pre_data
if not converted_pinyin: # 空字符串
return converted_pinyin
# 有声调,跳过
if _re_number.search(converted_pinyin):
return converted_pinyin
if style in self.NUMBER_AT_END:
return '{}5'.format(converted_pinyin)
# 找到应该在哪个字母上标声调
mark_index = right_mark_index(converted_pinyin)
before = converted_pinyin[:mark_index + 1]
after = converted_pinyin[mark_index + 1:]
return '{}5{}'.format(before, after)
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from typing import Any
from typing import Optional
from typing import Text
from typing import Tuple
from pypinyin.constants import Style
TStyle = Style
class NeutralToneWith5Mixin(object):
NUMBER_TONE = ... # type: Tuple[TStyle]
NUMBER_AT_END = ... # type: Tuple[TStyle]
def post_convert_style(self, han: Text, orig_pinyin: Text,
converted_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from pypinyin.style._tone_convert import ( # noqa
to_normal,
to_tone,
to_tone2,
to_tone3,
to_initials,
to_finals,
to_finals_tone,
to_finals_tone2,
to_finals_tone3,
tone_to_normal,
tone_to_tone2,
tone_to_tone3,
tone2_to_normal,
tone2_to_tone,
tone2_to_tone3,
tone3_to_normal,
tone3_to_tone,
tone3_to_tone2,
# 向后兼容
_improve_tone3,
_get_number_from_pinyin,
_v_to_u,
_fix_v_u,
_re_number,
) # noqa
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from typing import Any
from typing import Optional
from typing import Text
from typing import Tuple
_re_number = ... # type: Any
def to_normal(pinyin: Text, v_to_u: bool = ...) -> Text: ...
def to_tone(pinyin: Text) -> Text: ...
def to_tone2(pinyin: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_tone3(pinyin: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_initials(pinyin: Text, strict: bool = ...) -> Text: ...
def to_finals(pinyin: Text, strict: bool = ..., v_to_u: bool = ...) -> Text: ...
def to_finals_tone(pinyin: Text, strict: bool = ...) -> Text: ...
def to_finals_tone2(pinyin: Text, strict: bool = ..., v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_finals_tone3(pinyin: Text, strict: bool = ..., v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone_to_normal(tone: Text, v_to_u: bool = ...) -> Text: ...
def tone_to_tone2(tone: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone_to_tone3(tone: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone2_to_normal(tone2: Text, v_to_u: bool = ...) -> Text: ...
def tone2_to_tone(tone2: Text,) -> Text: ...
def tone2_to_tone3(tone2: Text, v_to_u: bool = ...) -> Text: ...
def tone3_to_normal(tone3: Text, v_to_u: bool = ...) -> Text: ...
def tone3_to_tone(tone3: Text) -> Text: ...
def tone3_to_tone2(tone3: Text, v_to_u: bool = ...) -> Text: ...
def _improve_tone3(tone3: Text, neutral_tone_with_five: bool = ...) -> Text: ...
def _get_number_from_pinyin(pinyin: Text) -> Optional[int]: ...
def _v_to_u(pinyin: Text, replace: bool = ...) -> Text: ...
def _fix_v_u(origin_py: Text, new_py: Text, v_to_u: bool) -> Text: ...
@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from pypinyin.contrib.tone_convert import tone_to_tone2, tone2_to_tone
_re_num = re.compile(r'\d')
class ToneSandhiMixin(object):
"""
按普通话变调规则处理拼音:
* https://en.wikipedia.org/wiki/Standard_Chinese_phonology#Tone_sandhi
* https://studycli.org/zh-CN/learn-chinese/tone-changes-in-mandarin/
""" # noqa
def post_pinyin(self, han, heteronym, pinyin_list, **kwargs):
ret = super(ToneSandhiMixin, self).post_pinyin(
han, heteronym, pinyin_list, **kwargs)
if ret is not None:
pinyin_list = ret
pinyin_list = self._third_tone(han, pinyin_list)
pinyin_list = self._bu(han, pinyin_list)
pinyin_list = self._yi(han, pinyin_list)
return pinyin_list
def _third_tone(self, han, pinyin_list):
"""
Third tone sandhi:
The principal rule of third tone sandhi is:
When there are two consecutive third-tone syllables, the first of them is pronounced with second tone.
For example, lǎoshǔ 老鼠 ("mouse") comes to be pronounced láoshǔ [lau̯˧˥ʂu˨˩]. It has been investigated whether the rising contour (˧˥) on the prior syllable is in fact identical to a normal second tone; it has been concluded that it is, at least in terms of auditory perception.[1]:237
When there are three or more third tones in a row, the situation becomes more complicated, since a third tone that precedes a second tone resulting from third tone sandhi may or may not be subject to sandhi itself. The results may depend on word boundaries, stress, and dialectal variations. General rules for three-syllable third-tone combinations can be formulated as follows:
If the first word is two syllables and the second word is one syllable, then the first two syllables become second tones. For example, bǎoguǎn hǎo 保管好 ("to take good care of") takes the pronunciation báoguán hǎo [pau̯˧˥kwan˧˥xau̯˨˩˦].
If the first word is one syllable, and the second word is two syllables, the second syllable becomes second tone, but the first syllable remains third tone. For example: lǎo bǎoguǎn 老保管 ("to take care of all the time") takes the pronunciation lǎo báoguǎn [lau̯˨˩pau̯˧˥kwan˨˩˦].
Some linguists have put forward more comprehensive systems of sandhi rules for multiple third tone sequences. For example, it is proposed[1]:248 that modifications are applied cyclically, initially within rhythmic feet (trochees; see below), and that sandhi "need not apply between two cyclic branches".
""" # noqa
tone2_pinyin_list = [tone_to_tone2(x[0]) for x in pinyin_list]
if '3' not in ''.join(tone2_pinyin_list):
return pinyin_list
changed = False
third_num = 0
for pinyin in tone2_pinyin_list:
if '3' in pinyin:
third_num += 1
else:
third_num = 0
if third_num == 2:
for i, v in enumerate(tone2_pinyin_list):
if '3' in v:
tone2_pinyin_list[i] = v.replace('3', '2')
changed = True
break
elif third_num > 2:
n = 1
for i, v in enumerate(tone2_pinyin_list):
if '3' in v:
if n == third_num:
break
tone2_pinyin_list[i] = v.replace('3', '2')
changed = True
n += 1
if changed:
return [[tone2_to_tone(x)] for x in tone2_pinyin_list]
return pinyin_list
def _bu(self, han, pinyin_list):
"""
For 不 bù:
不 is pronounced with second tone when followed by a fourth tone syllable.
Example: 不是 (bù+shì, "to not be") becomes búshì [pu˧˥ʂɻ̩˥˩]
In other cases, 不 is pronounced with fourth tone. However, when used between words in an A-not-A question, it may become neutral in tone (e.g., 是不是 shìbushì).
""" # noqa
if '' not in han:
return pinyin_list
tone2_pinyin_list = [tone_to_tone2(x[0]) for x in pinyin_list]
changed = False
for i, h in enumerate(han):
current_pinyin = tone2_pinyin_list[i]
if h == '' and i < len(han) - 1:
next_pinyin = tone2_pinyin_list[i+1]
if '4' in next_pinyin:
tone2_pinyin_list[i] = current_pinyin.replace('4', '2')
changed = True
else:
tone2_pinyin_list[i] = _re_num.sub('4', current_pinyin)
changed = True
elif h == '':
tone2_pinyin_list[i] = _re_num.sub('4', current_pinyin)
changed = True
if changed:
return [[tone2_to_tone(x)] for x in tone2_pinyin_list]
return pinyin_list
def _yi(self, han, pinyin_list):
"""
For 一 yī:
一 is pronounced with second tone when followed by a fourth tone syllable.
Example: 一定 (yī+dìng, "must") becomes yídìng [i˧˥tiŋ˥˩]
Before a first, second or third tone syllable, 一 is pronounced with fourth tone.
Examples:一天 (yī+tiān, "one day") becomes yìtiān [i˥˩tʰjɛn˥], 一年 (yī+nián, "one year") becomes yìnián [i˥˩njɛn˧˥], 一起 (yī+qǐ, "together") becomes yìqǐ [i˥˩t͡ɕʰi˨˩˦].
When final, or when it comes at the end of a multi-syllable word (regardless of the first tone of the next word), 一 is pronounced with first tone. It also has first tone when used as an ordinal number (or part of one), and when it is immediately followed by any digit (including another 一; hence both syllables of the word 一一 yīyī and its compounds have first tone).
When 一 is used between two reduplicated words, it may become neutral in tone (e.g. 看一看 kànyikàn ("to take a look of")).
""" # noqa
if '' not in han:
return pinyin_list
tone2_pinyin_list = [tone_to_tone2(x[0]) for x in pinyin_list]
changed = False
for i, h in enumerate(han):
current_pinyin = tone2_pinyin_list[i]
if h == '' and i < len(han) - 1:
next_pinyin = tone2_pinyin_list[i + 1]
if '4' in next_pinyin:
tone2_pinyin_list[i] = current_pinyin.replace('4', '2')
changed = True
else:
tone2_pinyin_list[i] = _re_num.sub('4', current_pinyin)
changed = True
elif h == '':
tone2_pinyin_list[i] = _re_num.sub('1', current_pinyin)
changed = True
if changed:
return [[tone2_to_tone(x)] for x in tone2_pinyin_list]
return pinyin_list
@@ -0,0 +1,21 @@
from typing import Any
from typing import List
from typing import Union
from typing import Callable
from typing import Optional
from typing import Text
from pypinyin.constants import Style
TStyle = Style
TErrors = Union[Callable[[Text], Text], Text]
TPinyinResult = List[List[Text]]
TErrorResult = Union[Text, List[Text], None]
TNoPinyinResult = Union[TPinyinResult, List[Text], Text, None]
class ToneSandhiMixin(object):
def post_pinyin(self, han: Text, heteronym: bool,
pinyin: TPinyinResult,
**kwargs: Any) -> Union[TPinyinResult, None]: ...
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class V2UMixin(object):
"""无声调相关拼音风格下的结果使用 ``ü`` 代替原来的 ``v``
使用方法::
from pypinyin import lazy_pinyin, Style
from pypinyin.contrib.uv import V2UMixin
from pypinyin.converter import DefaultConverter
from pypinyin.core import Pinyin
# 原来的结果中会使用 ``v`` 表示 ``ü``
print(lazy_pinyin('战略'))
# 输出:['zhan', 'lve']
class MyConverter(V2UMixin, DefaultConverter):
pass
my_pinyin = Pinyin(MyConverter())
pinyin = my_pinyin.pinyin
lazy_pinyin = my_pinyin.lazy_pinyin
# 新的结果中使用 ``ü`` 代替原来的 ``v``
print(lazy_pinyin('战略'))
# 输出: ['zhan', 'lüe']
print(pinyin('战略', style=Style.NORMAL))
# 输出:[['zhan'], ['lüe']]
"""
def post_convert_style(self, han, orig_pinyin, converted_pinyin,
style, strict, **kwargs):
pre_data = super(V2UMixin, self).post_convert_style(
han, orig_pinyin, converted_pinyin, style, strict, **kwargs)
if pre_data is not None:
converted_pinyin = pre_data
return converted_pinyin.replace('v', 'ü')
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
from typing import Any
from typing import Optional
from typing import Text
from pypinyin.constants import Style
TStyle = Style
class V2UMixin(object):
def post_convert_style(self, han: Text, orig_pinyin: Text,
converted_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
@@ -0,0 +1,390 @@
# -*- 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.exceptions import PinyinNotFoundException
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 == 'exception':
raise PinyinNotFoundException(chars)
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
@@ -0,0 +1,134 @@
from typing import Any
from typing import List
from typing import Union
from typing import Callable
from typing import Optional
from typing import Text
from pypinyin.constants import Style
TStyle = Style
TErrors = Union[Callable[[Text], Text], Text]
TPinyinResult = List[List[Text]]
TErrorResult = Union[Text, List[Text], None]
TNoPinyinResult = Union[TPinyinResult, List[Text], Text, None]
class Converter(object):
def convert(self, words: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool = ...,
**kwargs: Any) -> TPinyinResult: ...
class DefaultConverter(Converter):
def __init__(self, **kwargs: Any) -> None: ...
def convert(self, words: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool = ...,
**kwargs: Any) -> TPinyinResult: ...
def pre_convert_style(self, han: Text, orig_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
def convert_style(self, han: Text, orig_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Text: ...
def post_convert_style(self, han: Text, orig_pinyin: Text,
converted_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
def pre_handle_nopinyin(self, chars: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TNoPinyinResult: ...
def handle_nopinyin(self, chars: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool, **kwargs: Any
) -> TPinyinResult: ...
def post_handle_nopinyin(self, chars: Text, style: Style, heteronym: bool,
errors: TErrors, strict: bool,
pinyin: TNoPinyinResult, **kwargs: Any
) -> TNoPinyinResult: ...
def post_pinyin(self, han: Text, heteronym: bool,
pinyin: TPinyinResult,
**kwargs: Any) -> Union[TPinyinResult, None]: ...
def convert_styles(self, pinyin_list: TPinyinResult,
phrase: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool, **kwargs: Any,
) -> TPinyinResult: ...
def _phrase_pinyin(self, phrase: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TPinyinResult: ...
def _single_pinyin(self, han: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TPinyinResult: ...
def _convert_style(self, han: Text, pinyin: Text, style: TStyle,
strict: bool, default: Text, **kwargs: Any
) -> Text: ...
def _convert_nopinyin_chars(self, chars: Text, style: TStyle,
heteronym: bool, errors: TErrors,
strict: bool
) -> TNoPinyinResult: ...
class UltimateConverter(DefaultConverter):
def __init__(self, **kwargs: Any) -> None:
self._tone_sandhi = ...
self._neutral_tone_with_five = ...
self._v_to_u = ...
...
def convert(self, words: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool = ...,
**kwargs: Any) -> TPinyinResult: ...
def pre_convert_style(self, han: Text, orig_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
def convert_style(self, han: Text, orig_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Text: ...
def post_convert_style(self, han: Text, orig_pinyin: Text,
converted_pinyin: Text, style: TStyle,
strict: bool, **kwargs: Any) -> Optional[Text]: ...
def pre_handle_nopinyin(self, chars: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TNoPinyinResult: ...
def handle_nopinyin(self, chars: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool, **kwargs: Any
) -> TPinyinResult: ...
def post_handle_nopinyin(self, chars: Text, style: Style, heteronym: bool,
errors: TErrors, strict: bool,
pinyin: TNoPinyinResult, **kwargs: Any
) -> TNoPinyinResult: ...
def post_pinyin(self, han: Text, heteronym: bool,
pinyin: TPinyinResult,
**kwargs: Any) -> Union[TPinyinResult, None]: ...
def _phrase_pinyin(self, phrase: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TPinyinResult: ...
def _single_pinyin(self, han: Text, style: TStyle, heteronym: bool,
errors: TErrors, strict: bool
) -> TPinyinResult: ...
def _convert_style(self, han: Text, pinyin: Text, style: TStyle,
strict: bool, default: Text, **kwargs: Any
) -> Text: ...
def _convert_nopinyin_chars(self, chars: Text, style: TStyle,
heteronym: bool, errors: TErrors,
strict: bool
) -> TNoPinyinResult: ...
@@ -0,0 +1,388 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from itertools import chain
from pypinyin.compat import text_type
from pypinyin.constants import (
PHRASES_DICT, PINYIN_DICT, Style, RE_HANS
)
from pypinyin.converter import DefaultConverter, UltimateConverter
from pypinyin.contrib.tone_sandhi import ToneSandhiMixin
from pypinyin.contrib.tone_convert import tone2_to_tone
from pypinyin.seg import mmseg
from pypinyin.seg.simpleseg import seg
def load_single_dict(pinyin_dict, style='default'):
"""载入用户自定义的单字拼音库
:param pinyin_dict: 单字拼音库。比如: ``{0x963F: u"ā,ē"}``
:param style: pinyin_dict 参数值的拼音库风格. 支持 'default', 'tone2'
:type pinyin_dict: dict
"""
if style == 'tone2':
for k, v in pinyin_dict.items():
v = tone2_to_tone(v)
PINYIN_DICT[k] = v
else:
PINYIN_DICT.update(pinyin_dict)
mmseg.retrain(mmseg.seg)
def load_phrases_dict(phrases_dict, style='default'):
"""载入用户自定义的词语拼音库
:param phrases_dict: 词语拼音库。比如: ``{u"阿爸": [[u"ā"], [u""]]}``
:param style: phrases_dict 参数值的拼音库风格. 支持 'default', 'tone2'
:type phrases_dict: dict
"""
if style == 'tone2':
for k, value in phrases_dict.items():
v = [
list(map(tone2_to_tone, pys))
for pys in value
]
PHRASES_DICT[k] = v
else:
PHRASES_DICT.update(phrases_dict)
mmseg.retrain(mmseg.seg)
class Pinyin(object):
def __init__(self, converter=None, **kwargs):
self._converter = converter or DefaultConverter()
def pinyin(self, hans, style=Style.TONE, heteronym=False,
errors='default', strict=True, **kwargs):
"""将汉字转换为拼音,返回汉字的拼音列表。
:param hans: 汉字字符串( ``'你好吗'`` )或列表( ``['你好', '']`` ).
可以使用自己喜爱的分词模块对字符串进行分词处理,
只需将经过分词处理的字符串列表传进来就可以了。
:type hans: unicode 字符串或字符串列表
:param style: 指定拼音风格,默认是 :py:attr:`~pypinyin.Style.TONE` 风格。
更多拼音风格详见 :class:`~pypinyin.Style`
:param errors: 指定如何处理没有拼音的字符。详见 :ref:`handle_no_pinyin`
* ``'default'``: 保留原始字符
* ``'ignore'``: 忽略该字符
* ``'exception'``: 抛出异常
:class:`~pypinyin.exceptions.PinyinNotFoundException`
* ``'replace'``: 替换为去掉 ``\\u`` 的 unicode 编码字符串
(``'\\u90aa'`` => ``'90aa'``)
* callable 对象: 回调函数之类的可调用对象。
:param heteronym: 是否启用多音字
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:return: 拼音列表
:rtype: list
"""
# 对字符串进行分词处理
if isinstance(hans, text_type):
han_list = self.seg(hans)
else:
if isinstance(self._converter, UltimateConverter) or \
isinstance(self._converter, ToneSandhiMixin):
han_list = []
for h in hans:
if not RE_HANS.match(h):
han_list.extend(self.seg(h))
else:
han_list.append(h)
else:
han_list = chain(*(self.seg(x) for x in hans))
pys = []
for words in han_list:
pys.extend(
self._converter.convert(
words, style, heteronym, errors, strict=strict))
return pys
def lazy_pinyin(self, hans, style=Style.NORMAL,
errors='default', strict=True, **kwargs):
"""将汉字转换为拼音,返回不包含多音字结果的拼音列表.
与 :py:func:`~pypinyin.pinyin` 的区别是每个汉字的拼音是个字符串,
并且每个字只包含一个读音.
:param hans: 汉字字符串( ``'你好吗'`` )或列表( ``['你好', '']`` ).
可以使用自己喜爱的分词模块对字符串进行分词处理,
只需将经过分词处理的字符串列表传进来就可以了。
:type hans: unicode 字符串或字符串列表
:param style: 指定拼音风格,默认是 :py:attr:`~pypinyin.Style.NORMAL` 风格。
更多拼音风格详见 :class:`~pypinyin.Style`。
:param errors: 指定如何处理没有拼音的字符,详情请参考
:py:func:`~pypinyin.pinyin`
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:return: 拼音列表(e.g. ``['zhong', 'guo', 'ren']``)
:rtype: list
"""
return list(
chain(
*self.pinyin(
hans, style=style, heteronym=False,
errors=errors, strict=strict)))
def pre_seg(self, hans, **kwargs):
"""对字符串进行分词前将调用 ``pre_seg`` 方法对未分词的字符串做预处理。
默认原样返回传入的 ``hans``。
如果这个方法的返回值类型是 ``list``,表示返回的是一个分词后的结果,此时,
``seg`` 方法中将不再调用 ``seg_function`` 进行分词。
:param hans: 分词前的字符串
:return: ``None`` or ``list``
"""
pass
def seg(self, hans, **kwargs):
"""对汉字进行分词。
分词前会调用 ``pre_seg`` 方法,分词后会调用 ``post_seg`` 方法。
:param hans:
:return:
"""
pre_data = self.pre_seg(hans)
if isinstance(pre_data, list):
seg_data = pre_data
else:
seg_data = self.get_seg()(hans)
post_data = self.post_seg(hans, seg_data)
if isinstance(post_data, list):
return post_data
return seg_data
def get_seg(self, **kwargs):
"""获取分词函数。
:return: 分词函数
"""
return seg
def post_seg(self, hans, seg_data, **kwargs):
"""对字符串进行分词后将调用 ``post_seg`` 方法对分词后的结果做处理。
默认原样返回传入的 ``seg_data``。
如果这个方法的返回值类型是 ``list``,表示对分词结果做了二次处理,此时,
``seg`` 方法将以这个返回的数据作为返回值。
:param hans: 分词前的字符串
:param seg_data: 分词后的结果
:type seg_data: list
:return: ``None`` or ``list``
"""
pass
_default_convert = DefaultConverter()
_default_pinyin = Pinyin(_default_convert)
def to_fixed(pinyin, style, strict=True):
# 用于向后兼容,TODO: 废弃
return _default_convert.convert_style(
'', pinyin, style=style, strict=strict, default=pinyin)
_to_fixed = to_fixed
def handle_nopinyin(chars, errors='default', heteronym=True):
# 用于向后兼容,TODO: 废弃
return _default_convert.handle_nopinyin(
chars, style=None, errors=errors, heteronym=heteronym, strict=True)
def single_pinyin(han, style, heteronym, errors='default', strict=True):
# 用于向后兼容,TODO: 废弃
return _default_convert._single_pinyin(
han, style, heteronym, errors=errors, strict=strict)
def phrase_pinyin(phrase, style, heteronym, errors='default', strict=True):
# 用于向后兼容,TODO: 废弃
return _default_convert._phrase_pinyin(
phrase, style, heteronym, errors=errors, strict=strict)
def pinyin(hans, style=Style.TONE, heteronym=False,
errors='default', strict=True,
v_to_u=False, neutral_tone_with_five=False):
"""将汉字转换为拼音,返回汉字的拼音列表。
:param hans: 汉字字符串( ``'你好吗'`` )或列表( ``['你好', '']`` ).
可以使用自己喜爱的分词模块对字符串进行分词处理,
只需将经过分词处理的字符串列表传进来就可以了。
:type hans: unicode 字符串或字符串列表
:param style: 指定拼音风格,默认是 :py:attr:`~pypinyin.Style.TONE` 风格。
更多拼音风格详见 :class:`~pypinyin.Style`
:param errors: 指定如何处理没有拼音的字符。详见 :ref:`handle_no_pinyin`
* ``'default'``: 保留原始字符
* ``'ignore'``: 忽略该字符
* ``'replace'``: 替换为去掉 ``\\u`` 的 unicode 编码字符串
(``'\\u90aa'`` => ``'90aa'``)
* callable 对象: 回调函数之类的可调用对象。
:param heteronym: 是否启用多音字
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:param v_to_u: 无声调相关拼音风格下的结果是否使用 ``ü`` 代替原来的 ``v``
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:type v_to_u: bool
:param neutral_tone_with_five: 声调使用数字表示的相关拼音风格下的结果是否
使用 5 标识轻声
:type neutral_tone_with_five: bool
:return: 拼音列表
:rtype: list
:raise AssertionError: 当传入的字符串不是 unicode 字符时会抛出这个异常
Usage::
>>> from pypinyin import pinyin, Style
>>> import pypinyin
>>> pinyin('中心')
[['zhōng'], ['xīn']]
>>> pinyin('中心', heteronym=True) # 启用多音字模式
[['zhōng', 'zhòng'], ['xīn']]
>>> pinyin('中心', style=Style.FIRST_LETTER) # 设置拼音风格
[['z'], ['x']]
>>> pinyin('中心', style=Style.TONE2)
[['zho1ng'], ['xi1n']]
>>> pinyin('中心', style=Style.CYRILLIC)
[['чжун1'], ['синь1']]
>>> pinyin('战略', v_to_u=True, style=Style.NORMAL)
[['zhan'], ['lüe']]
>>> pinyin('衣裳', style=Style.TONE3, neutral_tone_with_five=True)
[['yi1'], ['shang5']]
"""
_pinyin = Pinyin(UltimateConverter(
v_to_u=v_to_u, neutral_tone_with_five=neutral_tone_with_five))
return _pinyin.pinyin(
hans, style=style, heteronym=heteronym, errors=errors, strict=strict)
def slug(hans, style=Style.NORMAL, heteronym=False, separator='-',
errors='default', strict=True):
"""将汉字转换为拼音,然后生成 slug 字符串.
:param hans: 汉字字符串( ``'你好吗'`` )或列表( ``['你好', '']`` ).
可以使用自己喜爱的分词模块对字符串进行分词处理,
只需将经过分词处理的字符串列表传进来就可以了。
:type hans: unicode 字符串或字符串列表
:param style: 指定拼音风格,默认是 :py:attr:`~pypinyin.Style.NORMAL` 风格。
更多拼音风格详见 :class:`~pypinyin.Style`
:param heteronym: 是否启用多音字
:param separator: 两个拼音间的分隔符/连接符
:param errors: 指定如何处理没有拼音的字符,详情请参考
:py:func:`~pypinyin.pinyin`
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:return: slug 字符串.
:raise AssertionError: 当传入的字符串不是 unicode 字符时会抛出这个异常
::
>>> import pypinyin
>>> from pypinyin import Style
>>> pypinyin.slug('中国人')
'zhong-guo-ren'
>>> pypinyin.slug('中国人', separator=' ')
'zhong guo ren'
>>> pypinyin.slug('中国人', style=Style.FIRST_LETTER)
'z-g-r'
>>> pypinyin.slug('中国人', style=Style.CYRILLIC)
'чжун1-го2-жэнь2'
"""
return separator.join(
chain(
*_default_pinyin.pinyin(
hans, style=style, heteronym=heteronym,
errors=errors, strict=strict
)
)
)
def lazy_pinyin(hans, style=Style.NORMAL, errors='default', strict=True,
v_to_u=False, neutral_tone_with_five=False, tone_sandhi=False):
"""将汉字转换为拼音,返回不包含多音字结果的拼音列表.
与 :py:func:`~pypinyin.pinyin` 的区别是返回的拼音是个字符串,
并且每个字只包含一个读音.
:param hans: 汉字字符串( ``'你好吗'`` )或列表( ``['你好', '']`` ).
可以使用自己喜爱的分词模块对字符串进行分词处理,
只需将经过分词处理的字符串列表传进来就可以了。
:type hans: unicode 字符串或字符串列表
:param style: 指定拼音风格,默认是 :py:attr:`~pypinyin.Style.NORMAL` 风格。
更多拼音风格详见 :class:`~pypinyin.Style`。
:param errors: 指定如何处理没有拼音的字符,详情请参考
:py:func:`~pypinyin.pinyin`
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:param v_to_u: 无声调相关拼音风格下的结果是否使用 ``ü`` 代替原来的 ``v``
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:type v_to_u: bool
:param neutral_tone_with_five: 声调使用数字表示的相关拼音风格下的结果是否
使用 5 标识轻声
:type neutral_tone_with_five: bool
:param tone_sandhi: 是否按照声调 `变调规则 <https://en.wikipedia.org/wiki/Standard_Chinese_phonology#Tone_sandhi>`__
对拼音进行处理
(使用预先通过分词库进行过分词后的结果作为 ``hans``
参数的值效果会更好,因为变调效果依赖分词效果)
:type tone_sandhi: bool
:return: 拼音列表(e.g. ``['zhong', 'guo', 'ren']``)
:rtype: list
:raise AssertionError: 当传入的字符串不是 unicode 字符时会抛出这个异常
Usage::
>>> from pypinyin import lazy_pinyin, Style
>>> import pypinyin
>>> lazy_pinyin('中心')
['zhong', 'xin']
>>> lazy_pinyin('中心', style=Style.TONE)
['zhōng', 'xīn']
>>> lazy_pinyin('中心', style=Style.FIRST_LETTER)
['z', 'x']
>>> lazy_pinyin('中心', style=Style.TONE2)
['zho1ng', 'xi1n']
>>> lazy_pinyin('中心', style=Style.CYRILLIC)
['чжун1', 'синь1']
>>> lazy_pinyin('战略', v_to_u=True)
['zhan', 'lüe']
>>> lazy_pinyin('衣裳', style=Style.TONE3, neutral_tone_with_five=True)
['yi1', 'shang5']
>>> lazy_pinyin('你好', style=Style.TONE2, tone_sandhi=True)
['ni2', 'ha3o']
""" # noqa
_pinyin = Pinyin(UltimateConverter(
v_to_u=v_to_u, neutral_tone_with_five=neutral_tone_with_five,
tone_sandhi=tone_sandhi))
return _pinyin.lazy_pinyin(
hans, style=style, errors=errors, strict=strict)
@@ -0,0 +1,118 @@
from typing import Any
from typing import List
from typing import Dict
from typing import Union
from typing import Callable
from typing import Optional
from typing import Text
from pypinyin.constants import Style
from pypinyin.converter import Converter
TStyle = Union[Style, Text]
TErrors = Union[Callable[[Text], Text], Text]
TPinyinResult = List[List[Text]]
def load_single_dict(pinyin_dict: Dict[int, Text],
style: str = ...) -> None: ...
def load_phrases_dict(phrases_dict: Dict[Text, List[List[Text]]],
style: str = ...
) -> None: ...
def to_fixed(pinyin: Text, style: TStyle,
strict: bool = ...) -> Text: ...
def _handle_nopinyin_char(chars: Text, errors: TErrors = ...
) -> Optional[Text]: ...
def handle_nopinyin(chars: Text, errors: TErrors = ..., heteronym: bool = ...
) -> List[List[Text]]: ...
def single_pinyin(han: Text, style: TStyle, heteronym: bool,
errors: TErrors = ...,
strict: bool = ...
) -> List[List[Text]]: ...
def phrase_pinyin(phrase: Text,
style: TStyle,
heteronym: bool,
errors: TErrors = ...,
strict: bool = ...
) -> List[List[Text]]: ...
def _pinyin(words: Text,
style: TStyle,
heteronym: bool,
errors: TErrors,
strict: bool = ...
) -> List[List[Text]]:...
def pinyin(hans: Union[List[Text], Text],
style: TStyle = ...,
heteronym: bool = ...,
errors: TErrors = ...,
strict: bool = ...,
v_to_u: bool = ...,
neutral_tone_with_five: bool = ...
) -> List[List[Text]]: ...
def slug(hans: Union[List[Text], Text],
style: TStyle = ...,
heteronym: bool = ...,
separator: Text = ...,
errors: TErrors = ...,
strict: bool = ...
) -> Text: ...
def lazy_pinyin(hans: Union[List[Text], Text],
style: TStyle = ...,
errors: TErrors = ...,
strict: bool = ...,
v_to_u: bool = ...,
neutral_tone_with_five: bool = ...,
tone_sandhi: bool = ...
) -> List[Text]: ...
class Pinyin(object):
def __init__(self, converter: Converter = ..., **kwargs: Any) -> None:
self._converter = ... # type: Converter
def pinyin(self, hans: Union[List[Text], Text],
style: TStyle = ...,
heteronym: bool = ...,
errors: TErrors = ...,
strict: bool = ...,
**kwargs: Any
) -> TPinyinResult: ...
def lazy_pinyin(self, hans: Union[List[Text], Text],
style: TStyle = ...,
errors: TErrors = ...,
strict: bool = ...,
**kwargs: Any
) -> List[Text]: ...
def pre_seg(self, hans: Text,
**kwargs: Any) -> Optional[List[Text]]: ...
def post_seg(self, hans: Text, seg_data: List[Text],
**kwargs: Any) -> Optional[List[Text]]: ...
def seg(self, hans: Text, **kwargs: Any) -> List[Text]: ...
def get_seg(self, **kwargs: Any) -> Callable[[Text], List[Text]]: ...
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class PinyinNotFoundException(Exception):
#: 异常信息
message = ''
#: 不包含拼音的字符串
chars = ''
def __init__(self, chars):
self.message = 'No pinyin found for character "{}"'.format(chars)
self.chars = chars
super(PinyinNotFoundException, self).__init__(self.message)
@@ -0,0 +1,8 @@
from typing import Union, Text, ByteString
class PinyinNotFoundException(Exception):
message: Union[Text, ByteString]
chars: Union[Text, ByteString]
def __init__(self, chars: Union[Text, ByteString]) -> None: ...
@@ -0,0 +1,53 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# 带声调字符。
phonetic_symbol = {
"ā": "a1",
"á": "a2",
"ǎ": "a3",
"à": "a4",
"ē": "e1",
"é": "e2",
"ě": "e3",
"è": "e4",
"ō": "o1",
"ó": "o2",
"ǒ": "o3",
"ò": "o4",
"ī": "i1",
"í": "i2",
"ǐ": "i3",
"ì": "i4",
"ū": "u1",
"ú": "u2",
"ǔ": "u3",
"ù": "u4",
# üe
"ü": "v",
"ǖ": "v1",
"ǘ": "v2",
"ǚ": "v3",
"ǜ": "v4",
"ń": "n2",
"ň": "n3",
"ǹ": "n4",
"": "m1", # len('m̄') == 2
"ḿ": "m2",
"": "m4", # len("m̀") == 2
"ê̄": "ê1", # len('ê̄') == 2
"ế": "ê2",
"ê̌": "ê3", # len('ê̌') == 2
"": "ê4",
}
phonetic_symbol_reverse = dict((v, k) for k, v in phonetic_symbol.items())
@@ -0,0 +1,5 @@
from typing import Dict, Text
phonetic_symbol = ... # type: Dict[Text, Text]
phonetic_symbol_reverse = ... # type: Dict[Text, Text]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from codecs import open
import json
import os
_current_dir = os.path.dirname(os.path.realpath(__file__))
_json_path = os.path.join(_current_dir, 'phrases_dict.json')
phrases_dict = {}
def _load_phrases_dict():
global phrases_dict
with open(_json_path, encoding='utf8') as fp:
phrases_dict = json.loads(fp.read())
_load_phrases_dict()
@@ -0,0 +1,3 @@
from typing import Dict, List, Text
phrases_dict = ... # type: Dict[Text, List[List[Text]]]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from codecs import open
import json
import os
_current_dir = os.path.dirname(os.path.realpath(__file__))
_json_path = os.path.join(_current_dir, 'pinyin_dict.json')
pinyin_dict = {}
def _load_pinyin_dict():
global pinyin_dict
with open(_json_path, encoding='utf8') as fp:
pinyin_dict = json.loads(fp.read())
for k, v in pinyin_dict.copy().items():
del pinyin_dict[k]
pinyin_dict[int(k)] = v
_load_pinyin_dict()
@@ -0,0 +1,3 @@
from typing import Dict, Text
pinyin_dict = ... # type: Dict[int, Text]
@@ -0,0 +1,144 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from argparse import ArgumentParser
import logging
import sys
import pypinyin
from pypinyin.compat import PY2
style_map = {
'NORMAL': pypinyin.Style.NORMAL,
'zhao': pypinyin.Style.NORMAL,
'TONE': pypinyin.Style.TONE,
'zh4ao': pypinyin.Style.TONE,
'TONE2': pypinyin.Style.TONE2,
'zha4o': pypinyin.Style.TONE2,
'TONE3': pypinyin.Style.TONE3,
'zhao4': pypinyin.Style.TONE3,
'INITIALS': pypinyin.Style.INITIALS,
'zh': pypinyin.Style.INITIALS,
'FIRST_LETTER': pypinyin.Style.FIRST_LETTER,
'z': pypinyin.Style.FIRST_LETTER,
'FINALS': pypinyin.Style.FINALS,
'ao': pypinyin.Style.FINALS,
'FINALS_TONE': pypinyin.Style.FINALS_TONE,
'4ao': pypinyin.Style.FINALS_TONE,
'FINALS_TONE2': pypinyin.Style.FINALS_TONE2,
'a4o': pypinyin.Style.FINALS_TONE2,
'FINALS_TONE3': pypinyin.Style.FINALS_TONE3,
'ao4': pypinyin.Style.FINALS_TONE3,
'BOPOMOFO': pypinyin.Style.BOPOMOFO,
'BOPOMOFO_FIRST': pypinyin.Style.BOPOMOFO_FIRST,
'CYRILLIC': pypinyin.Style.CYRILLIC,
'CYRILLIC_FIRST': pypinyin.Style.CYRILLIC_FIRST,
}
func_map = {
'pinyin': pypinyin.pinyin,
'slug': pypinyin.slug,
}
default_style = 'zh4ao'
class NullWriter(object):
"""数据流黑洞,类似 linux/unix 下 /dev/null 的效果。"""
def write(self, string):
pass
def get_parser():
parser = ArgumentParser(description='convert chinese to pinyin.')
parser.add_argument('-V', '--version', action='version',
version='{0} {1}'.format(
pypinyin.__title__, pypinyin.__version__
))
# 要执行的函数名称
parser.add_argument('-f', '--func',
help='function name (default: "pinyin")',
choices=['pinyin', 'slug'],
default='pinyin')
# 拼音风格
parser.add_argument(
'-s', '--style',
help='pinyin style (default: "{0}")'.format(default_style),
choices=style_map.keys(), default=default_style
)
parser.add_argument('-p', '--separator',
help='slug separator (default: "-")',
default='-')
parser.add_argument('-e', '--errors',
help=('how to handle none-pinyin string'
' (default: "default")'),
choices=['default', 'ignore', 'replace'],
default='default')
# 输出多音字
parser.add_argument('-m', '--heteronym', help='enable heteronym',
action='store_true')
# 要查询的汉字
parser.add_argument('hans', nargs='+', help='chinese string')
return parser
def main():
# 禁用除 CRITICAL 外的日志消息
logging.disable(logging.CRITICAL)
# read hans from stdin
if not sys.stdin.isatty():
pipe_data = sys.stdin.read().strip()
else:
pipe_data = ''
args = sys.argv[1:]
if pipe_data:
args.append(pipe_data)
# 获取命令行选项和参数
parser = get_parser()
options = parser.parse_args(args)
if PY2:
hans = [
han.decode(sys.stdin.encoding or 'utf-8') for han in options.hans
]
else:
hans = options.hans
func = getattr(pypinyin, options.func)
style = style_map[options.style]
heteronym = options.heteronym
separator = options.separator
errors = options.errors
func_kwargs = {
'pinyin': {'heteronym': heteronym, 'errors': errors},
'slug': {'heteronym': heteronym, 'separator': separator,
'errors': errors},
}
if PY2:
kwargs = func_kwargs[func.func_name]
else:
kwargs = func_kwargs[func.__name__]
# 重设标准输出流和标准错误流
# 不输出任何字符,防止污染命令行命令的输出结果
# 其实主要是为了干掉 jieba 内的 print 语句 ;)
sys.stdout = sys.stderr = NullWriter()
results = [func(han, style=style, **kwargs) for han in hans]
# 恢复默认
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
for result in results:
if not result:
print('')
elif result and isinstance(result, (list, tuple)):
if isinstance(result[0], (list, tuple)):
print(' '.join([','.join(s) for s in result]))
else:
print(result)
else:
print(result)
if __name__ == '__main__':
main()
@@ -0,0 +1,12 @@
from argparse import ArgumentParser
from typing import Union, Text, ByteString
class NullWriter(object):
def write(self, string: Union[Text, ByteString]) -> None: ...
def get_parser() -> ArgumentParser: ...
def main() -> None: ...
@@ -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]: ...
@@ -0,0 +1,161 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
处理汉语拼音方案中的一些特殊情况
汉语拼音方案:
* https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E6%B1%89%E8%AF%AD%E6%8B%BC%E9%9F%B3%E6%96%B9%E6%A1%88
* http://www.moe.edu.cn/s78/A19/yxs_left/moe_810/s230/195802/t19580201_186000.html
""" # noqa
from __future__ import unicode_literals
import re
from pypinyin.style._constants import _FINALS
# u -> ü
UV_MAP = {
'u': 'ü',
'ū': 'ǖ',
'ú': 'ǘ',
'ǔ': 'ǚ',
'ù': 'ǜ',
}
U_TONES = set(UV_MAP.keys())
# ü行的韵跟声母j,q,x拼的时候,写成ju(居),qu(区),xu(虚)
UV_RE = re.compile(
r'^(j|q|x)({tones})(.*)$'.format(
tones='|'.join(UV_MAP.keys())))
I_TONES = set(['i', 'ī', 'í', 'ǐ', 'ì'])
# iu -> iou
IU_MAP = {
'iu': 'iou',
'': 'ioū',
'': 'ioú',
'': 'ioǔ',
'': 'ioù',
}
IU_TONES = set(IU_MAP.keys())
IU_RE = re.compile(r'^([a-z]+)({tones})$'.format(tones='|'.join(IU_TONES)))
# ui -> uei
UI_MAP = {
'ui': 'uei',
'': 'ueī',
'': 'ueí',
'': 'ueǐ',
'': 'ueì',
}
UI_TONES = set(UI_MAP.keys())
UI_RE = re.compile(r'([a-z]+)({tones})$'.format(tones='|'.join(UI_TONES)))
# un -> uen
UN_MAP = {
'un': 'uen',
'ūn': 'ūen',
'ún': 'úen',
'ǔn': 'ǔen',
'ùn': 'ùen',
}
UN_TONES = set(UN_MAP.keys())
UN_RE = re.compile(r'([a-z]+)({tones})$'.format(tones='|'.join(UN_TONES)))
def convert_zero_consonant(pinyin):
"""零声母转换,还原原始的韵母
i行的韵母,前面没有声母的时候,写成yi(衣),ya(呀),ye(耶)yao(腰)
you(忧)yan(烟)yin(因)yang(央)ying(英)yong(雍)。
u行的韵母,前面没有声母的时候,写成wu(乌),wa(蛙),wo(窝)wai(歪)
wei(威)wan(弯)wen(温)wang(汪)weng(翁)。
ü行的韵母,前面没有声母的时候,写成yu(迂),yue(约)yuan(冤)
yun(晕);ü上两点省略。
"""
raw_pinyin = pinyin
# y: yu -> v, yi -> i, y -> i
if raw_pinyin.startswith('y'):
# 去除 y 后的拼音
no_y_py = pinyin[1:]
first_char = no_y_py[0] if len(no_y_py) > 0 else None
# yu -> ü: yue -> üe
if first_char in U_TONES:
pinyin = UV_MAP[first_char] + pinyin[2:]
# yi -> i: yi -> i
elif first_char in I_TONES:
pinyin = no_y_py
# y -> i: ya -> ia
else:
pinyin = 'i' + no_y_py
# w: wu -> u, w -> u
if raw_pinyin.startswith('w'):
# 去除 w 后的拼音
no_w_py = pinyin[1:]
first_char = no_w_py[0] if len(no_w_py) > 0 else None
# wu -> u: wu -> u
if first_char in U_TONES:
pinyin = pinyin[1:]
# w -> u: wa -> ua
else:
pinyin = 'u' + pinyin[1:]
# 确保不会出现韵母表中不存在的韵母
if pinyin not in _FINALS:
return raw_pinyin
return pinyin
def convert_uv(pinyin):
"""ü 转换,还原原始的韵母
ü行的韵跟声母j,q,x拼的时候,写成ju(居),qu(区),xu(虚)
ü上两点也省略;但是跟声母n,l拼的时候,仍然写成nü(女),lü(吕)。
"""
return UV_RE.sub(
lambda m: ''.join((m.group(1), UV_MAP[m.group(2)], m.group(3))),
pinyin)
def convert_iou(pinyin):
"""iou 转换,还原原始的韵母
iou,uei,uen前面加声母的时候,写成iu,ui,un。
例如niu(牛)gui(归)lun(论)。
"""
return IU_RE.sub(lambda m: m.group(1) + IU_MAP[m.group(2)], pinyin)
def convert_uei(pinyin):
"""uei 转换,还原原始的韵母
iou,uei,uen前面加声母的时候,写成iu,ui,un。
例如niu(牛)gui(归)lun(论)。
"""
return UI_RE.sub(lambda m: m.group(1) + UI_MAP[m.group(2)], pinyin)
def convert_uen(pinyin):
"""uen 转换,还原原始的韵母
iou,uei,uen前面加声母的时候,写成iu,ui,un。
例如niu(牛)gui(归)lun(论)。
"""
return UN_RE.sub(lambda m: m.group(1) + UN_MAP[m.group(2)], pinyin)
def convert_finals(pinyin):
"""还原原始的韵母"""
pinyin = convert_zero_consonant(pinyin)
pinyin = convert_uv(pinyin)
pinyin = convert_iou(pinyin)
pinyin = convert_uei(pinyin)
pinyin = convert_uen(pinyin)
return pinyin
@@ -0,0 +1,18 @@
from typing import Text
def convert_zero_consonant(pinyin: Text) -> Text: ...
def convert_uv(pinyin: Text) -> Text: ...
def convert_iou(pinyin: Text) -> Text: ...
def convert_uei(pinyin: Text) -> Text: ...
def convert_uen(pinyin: Text) -> Text: ...
def convert_finals(pinyin: Text) -> Text: ...
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
from functools import wraps
# 存储各拼音风格对应的实现
_registry = {}
def convert(pinyin, style, strict, default=None, **kwargs):
"""根据拼音风格把原始拼音转换为不同的格式
:param pinyin: 原始有声调的单个拼音
:type pinyin: unicode
:param style: 拼音风格
:param strict: 只获取声母或只获取韵母相关拼音风格的返回结果
是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:type strict: bool
:param default: 拼音风格对应的实现不存在时返回的默认值
:param kwargs: 兼容后续可能会新增的关键字参数。当前包含如下关键字参数:
``han``: 当前拼音对应的原始汉字。
:return: 按照拼音风格进行处理过后的拼音字符串
:rtype: unicode
"""
if style in _registry:
return _registry[style](pinyin, strict=strict, **kwargs)
return default
def register(style, func=None):
"""注册一个拼音风格实现。
自定义的函数应当使用 ``**kwargs`` 来兼容后续可能会新增的关键字参数,
当前默认会传递如下参数:
* ``pinyin``: 原始有声调的单个拼音
* ``strict``: 是否开启 strict 模式
* ``han``: 当前拼音对应的原始汉字
::
@register('echo')
def echo(pinyin, **kwargs):
return pinyin
# or
register('echo', echo)
"""
if func is not None:
_registry[style] = func
return
def decorator(func):
_registry[style] = func
@wraps(func)
def wrapper(pinyin, **kwargs):
return func(pinyin, **kwargs)
return wrapper
return decorator
def auto_discover():
"""自动注册内置的拼音风格实现"""
from pypinyin.style import ( # noqa
initials,
tone,
finals,
bopomofo,
cyrillic,
wadegiles,
others,
gwoyeu,
braille_mainland,
)
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from typing import Any, Optional, Callable, Dict, Text, Union
from pypinyin.constants import Style
TStyle = Style
TRegisterFunc = Optional[Callable[[Text, Dict[Any, Any]], Text]]
TWrapperFunc = Optional[Callable[[Text, Dict[Any, Any]], Text]]
_registry = {} # type: Dict[Union[TStyle, int, str, Any], TRegisterFunc]
def convert(pinyin: Text, style: TStyle, strict: bool,
default: Optional[Text] = ..., **kwargs: Any) -> Text: ...
def register(style: Union[TStyle, int, str, Any],
func: TRegisterFunc = ...) -> TWrapperFunc: ...
def auto_discover() -> None: ...
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from pypinyin import phonetic_symbol
# 声母表
_INITIALS = [
'b',
'p',
'm',
'f',
'd',
't',
'n',
'l',
'g',
'k',
'h',
'j',
'q',
'x',
'zh',
'ch',
'sh',
'r',
'z',
'c',
's',
]
# 声母表, 把 y, w 也当作声母
_INITIALS_NOT_STRICT = _INITIALS + ['y', 'w']
# 韵母表
_FINALS = [
'i',
'u',
'ü',
'a',
'ia',
'ua',
'o',
'uo',
'e',
'ie',
'üe',
'ai',
'uai',
'ei',
'uei',
'ao',
'iao',
'ou',
'iou',
'an',
'ian',
'uan',
'üan',
'en',
'in',
'uen',
'ün',
'ang',
'iang',
'uang',
'eng',
'ing',
'ueng',
'ong',
'iong',
'er',
'ê',
]
# 带声调字符与数字表示声调的对应关系
PHONETIC_SYMBOL_DICT = phonetic_symbol.phonetic_symbol.copy()
PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE = dict(
(k, v)
for k, v in PHONETIC_SYMBOL_DICT.items()
if len(k) > 1
)
# 匹配带声调字符的正则表达式
RE_PHONETIC_SYMBOL = re.compile(
r'[{0}]'.format(
re.escape(''.join(x for x in PHONETIC_SYMBOL_DICT if len(x) == 1))
)
)
# 匹配使用数字标识声调的字符的正则表达式
RE_TONE2 = re.compile(r'([aeoiuvnmêü])([1-5])$')
# 匹配 TONE2 中标识韵母声调的正则表达式
RE_TONE3 = re.compile(r'^([a-zêü]+)([1-5])([a-zêü]*)$')
# 匹配单个数字
RE_NUMBER = re.compile(r'\d')
@@ -0,0 +1,22 @@
from typing import Any, List, Dict, Text
_INITIALS = ... # type: List[Text]
_INITIALS_NOT_STRICT = ... # type: List[Text]
_FINALS = ... # type: List[Text]
PHONETIC_SYMBOL_DICT = ... # type: Dict[Text, Text]
PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE = ... # type: Dict[Text, Text]
RE_PHONETIC_SYMBOL = ... # type : Any
RE_TONE2 = ... # type : Any
RE_TONE3 = ... # type : Any
RE_NUMBER = ... # type: Any
@@ -0,0 +1,577 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from pypinyin import phonetic_symbol
from pypinyin.style._tone_rule import right_mark_index
from pypinyin.style._constants import RE_TONE3, RE_TONE2
from pypinyin.style.tone import converter
from pypinyin.style._utils import (
get_initials, replace_symbol_to_no_symbol,
get_finals, replace_symbol_to_number
)
_re_number = re.compile(r'\d')
def to_normal(pinyin, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_normal
>>> to_normal('zhōng')
'zhong'
>>> to_normal('zho1ng')
'zhong'
>>> to_normal('zhong1')
'zhong'
>>> to_normal('lüè')
'lve'
>>> to_normal('lüè', v_to_u=True)
'lüe'
"""
s = tone_to_tone2(pinyin, v_to_u=True)
s = tone2_to_normal(s)
return _fix_v_u(pinyin, s, v_to_u=v_to_u)
def to_tone(pinyin):
"""将 :py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:return: :py:attr:`~pypinyin.Style.TONE` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_tone
>>> to_tone('zho1ng')
'zhōng'
>>> to_tone('zhong1')
'zhōng'
"""
pinyin = pinyin.replace('v', 'ü')
if not _re_number.search(pinyin):
return pinyin
s = tone_to_tone2(pinyin)
s = tone2_to_tone(s)
return s
def to_tone2(pinyin, v_to_u=False, neutral_tone_with_five=False, **kwargs):
"""将 :py:attr:`~pypinyin.Style.TONE` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:param kwargs: 用于兼容老版本的 ``neutral_tone_with_5`` 参数,当传入
``neutral_tone_with_5`` 参数时,
将覆盖 ``neutral_tone_with_five`` 的值。
:return: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_tone2
>>> to_tone2('zhōng')
'zho1ng'
>>> to_tone2('zhong1')
'zho1ng'
>>> to_tone2('shang')
'shang'
>>> to_tone2('shang', neutral_tone_with_five=True)
'sha5ng'
>>> to_tone2('lüè')
'lve4'
>>> to_tone2('lüè', v_to_u=True)
'lüe4'
"""
if kwargs.get('neutral_tone_with_5', None) is not None:
neutral_tone_with_five = kwargs['neutral_tone_with_5']
pinyin = pinyin.replace('5', '')
s = tone_to_tone3(
pinyin, v_to_u=True, neutral_tone_with_five=neutral_tone_with_five)
s = tone3_to_tone2(s)
return _fix_v_u(pinyin, s, v_to_u)
def to_tone3(pinyin, v_to_u=False, neutral_tone_with_five=False, **kwargs):
"""将 :py:attr:`~pypinyin.Style.TONE` 或
:py:attr:`~pypinyin.Style.TONE2` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE` 或
:py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:param kwargs: 用于兼容老版本的 ``neutral_tone_with_5`` 参数,当传入
``neutral_tone_with_5`` 参数时,
将覆盖 ``neutral_tone_with_five`` 的值。
:return: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_tone3
>>> to_tone3('zhōng')
'zhong1'
>>> to_tone3('zho1ng')
'zhong1'
>>> to_tone3('shang')
'shang'
>>> to_tone3('shang', neutral_tone_with_five=True)
'shang5'
>>> to_tone3('lüè')
'lve4'
>>> to_tone3('lüè', v_to_u=True)
'lüe4'
"""
if kwargs.get('neutral_tone_with_5', None) is not None:
neutral_tone_with_five = kwargs['neutral_tone_with_5']
pinyin = pinyin.replace('5', '')
s = tone_to_tone2(
pinyin, v_to_u=True, neutral_tone_with_five=neutral_tone_with_five)
s = tone2_to_tone3(s)
return _fix_v_u(pinyin, s, v_to_u)
def to_initials(pinyin, strict=True):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 、
:py:attr:`~pypinyin.Style.TONE3` 或
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音转换为
:py:attr:`~pypinyin.Style.INITIALS` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 、
:py:attr:`~pypinyin.Style.TONE3` 或
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param strict: 返回结果是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:return: :py:attr:`~pypinyin.Style.INITIALS` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_initials
>>> to_initials('zhōng')
'zh'
"""
return get_initials(pinyin, strict=strict)
def to_finals(pinyin, strict=True, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 、
:py:attr:`~pypinyin.Style.TONE3` 或
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音转换为
:py:attr:`~pypinyin.Style.FINALS` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 、
:py:attr:`~pypinyin.Style.TONE3` 或
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param strict: 返回结果是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.FINALS` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_finals
>>> to_finals('zhōng')
'ong'
"""
new_pinyin = replace_symbol_to_no_symbol(pinyin).replace('v', 'ü')
finals = get_finals(new_pinyin, strict=strict)
finals = _fix_v_u(finals, finals, v_to_u)
return finals
def to_finals_tone(pinyin, strict=True):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.FINALS_TONE` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param strict: 返回结果是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:return: :py:attr:`~pypinyin.Style.FINALS_TONE` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_finals_tone
>>> to_finals_tone('zhōng')
'ōng'
"""
finals = to_finals_tone2(pinyin, strict=strict)
finals = tone2_to_tone(finals)
return finals
def to_finals_tone2(pinyin, strict=True, v_to_u=False,
neutral_tone_with_five=False):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.FINALS_TONE2` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param strict: 返回结果是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:return: :py:attr:`~pypinyin.Style.FINALS_TONE2` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_finals_tone2
>>> to_finals_tone2('zhōng')
'o1ng'
"""
pinyin = pinyin.replace('5', '')
finals = to_finals_tone3(pinyin, strict=strict, v_to_u=v_to_u,
neutral_tone_with_five=neutral_tone_with_five)
finals = tone3_to_tone2(finals, v_to_u=v_to_u)
return finals
def to_finals_tone3(pinyin, strict=True, v_to_u=False,
neutral_tone_with_five=False):
"""将 :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.FINALS_TONE3` 风格的拼音
:param pinyin: :py:attr:`~pypinyin.Style.TONE`、
:py:attr:`~pypinyin.Style.TONE2` 或
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param strict: 返回结果是否严格遵照《汉语拼音方案》来处理声母和韵母,
详见 :ref:`strict`
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:return: :py:attr:`~pypinyin.Style.FINALS_TONE3` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import to_finals_tone3
>>> to_finals_tone3('zhōng')
'ong1'
"""
pinyin = pinyin.replace('5', '')
finals = to_finals(pinyin, strict=strict, v_to_u=v_to_u)
if not finals:
return finals
numbers = _re_number.findall(replace_symbol_to_number(pinyin))
if not numbers:
if neutral_tone_with_five:
numbers = ['5']
else:
return finals
number = numbers[0]
finals = finals + number
return finals
def tone_to_normal(tone, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE` 风格的拼音转换为
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param tone: :py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone_to_normal
>>> tone_to_normal('zhōng')
'zhong'
>>> tone_to_normal('lüè')
'lve'
>>> tone_to_normal('lüè', v_to_u=True)
'lüe'
"""
s = tone_to_tone2(tone, v_to_u=v_to_u)
s = _re_number.sub('', s)
return _v_to_u(s, v_to_u)
def tone_to_tone2(tone, v_to_u=False, neutral_tone_with_five=False, **kwargs):
"""将 :py:attr:`~pypinyin.Style.TONE` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param tone: :py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:param kwargs: 用于兼容老版本的 ``neutral_tone_with_5`` 参数,当传入
``neutral_tone_with_5`` 参数时,
将覆盖 ``neutral_tone_with_five`` 的值。
:return: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone_to_tone2
>>> tone_to_tone2('zhōng')
'zho1ng'
>>> tone_to_tone2('shang')
'shang'
>>> tone_to_tone2('shang', neutral_tone_with_5=True)
'sha5ng'
>>> tone_to_tone2('lüè')
'lve4'
>>> tone_to_tone2('lüè', v_to_u=True)
'lüe4'
"""
if kwargs.get('neutral_tone_with_5', None) is not None:
neutral_tone_with_five = kwargs['neutral_tone_with_5']
tone3 = tone_to_tone3(
tone, v_to_u=v_to_u, neutral_tone_with_five=neutral_tone_with_five)
s = tone3_to_tone2(tone3)
return _v_to_u(s, v_to_u)
def tone_to_tone3(tone, v_to_u=False, neutral_tone_with_five=False, **kwargs):
"""将 :py:attr:`~pypinyin.Style.TONE` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param tone: :py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:param neutral_tone_with_five: 是否使用 ``5`` 标识轻声
:param kwargs: 用于兼容老版本的 ``neutral_tone_with_5`` 参数,当传入
``neutral_tone_with_5`` 参数时,
将覆盖 ``neutral_tone_with_five`` 的值。
:return: :py:attr:`~pypinyin.Style.TONE3` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone_to_tone3
>>> tone_to_tone3('zhōng')
'zhong1'
>>> tone_to_tone3('shang')
'shang'
>>> tone_to_tone3('shang', neutral_tone_with_five=True)
'shang5'
>>> tone_to_tone3('lüè')
'lve4'
>>> tone_to_tone3('lüè', v_to_u=True)
'lüe4'
"""
if kwargs.get('neutral_tone_with_5', None) is not None:
neutral_tone_with_five = kwargs['neutral_tone_with_5']
tone3 = converter.to_tone3(tone)
s = _improve_tone3(tone3, neutral_tone_with_five=neutral_tone_with_five)
return _v_to_u(s, v_to_u)
def tone2_to_normal(tone2, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE2` 风格的拼音转换为
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param tone2: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: Style.NORMAL 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone2_to_normal
>>> tone2_to_normal('zho1ng')
'zhong'
>>> tone2_to_normal('lüe4')
'lve'
>>> tone2_to_normal('lüe4', v_to_u=True)
'lüe'
"""
s = _re_number.sub('', tone2)
s = _v_to_u(s, v_to_u)
return _fix_v_u(tone2, s, v_to_u=v_to_u)
def tone2_to_tone(tone2):
"""将 :py:attr:`~pypinyin.Style.TONE2` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param tone2: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:return: Style.TONE 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone2_to_tone
>>> tone2_to_tone('zho1ng')
'zhōng'
"""
regex = re.compile(RE_TONE2.pattern.replace('$', ''))
d = phonetic_symbol.phonetic_symbol_reverse
string = tone2.replace('ü', 'v').replace('5', '').replace('0', '')
def _replace(m):
s = m.group(0)
return d.get(s) or s
return regex.sub(_replace, string).replace('v', 'ü')
def tone2_to_tone3(tone2, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE2` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param tone2: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.TONE3` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone2_to_tone3
>>> tone2_to_tone3('zho1ng')
'zhong1'
>>> tone2_to_tone3('lüe4')
'lve4'
>>> tone2_to_tone3('lüe4', v_to_u=True)
'lüe4'
"""
tone3 = RE_TONE3.sub(r'\1\3\2', tone2)
return _fix_v_u(tone2, tone3, v_to_u=v_to_u)
def tone3_to_normal(tone3, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
:param tone3: :py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.NORMAL` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone3_to_normal
>>> tone3_to_normal('zhong1')
'zhong'
>>> tone3_to_normal('lüe4')
'lve'
>>> tone3_to_normal('lüe4', v_to_u=True)
'lüe'
"""
s = _re_number.sub('', tone3)
s = _v_to_u(s, v_to_u)
return _fix_v_u(tone3, s, v_to_u=v_to_u)
def tone3_to_tone(tone3):
"""将 :py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE` 风格的拼音
:param tone3: :py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:return: :py:attr:`~pypinyin.Style.TONE` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone3_to_tone
>>> tone3_to_tone('zhong1')
'zhōng'
"""
tone2 = tone3_to_tone2(tone3, v_to_u=True)
return tone2_to_tone(tone2)
def tone3_to_tone2(tone3, v_to_u=False):
"""将 :py:attr:`~pypinyin.Style.TONE3` 风格的拼音转换为
:py:attr:`~pypinyin.Style.TONE2` 风格的拼音
:param tone3: :py:attr:`~pypinyin.Style.TONE3` 风格的拼音
:param v_to_u: 是否使用 ``ü`` 代替原来的 ``v``,
当为 False 时结果中将使用 ``v`` 表示 ``ü``
:return: :py:attr:`~pypinyin.Style.TONE2` 风格的拼音
Usage::
>>> from pypinyin.contrib.tone_convert import tone3_to_tone2
>>> tone3_to_tone2('zhong1')
'zho1ng'
>>> tone3_to_tone2('lüe4')
'lve4'
>>> tone3_to_tone2('lüe4', v_to_u=True)
'lüe4'
"""
no_number_tone3 = tone3_to_normal(tone3)
mark_index = right_mark_index(no_number_tone3)
if mark_index is None:
mark_index = len(no_number_tone3) - 1
before = no_number_tone3[:mark_index + 1]
after = no_number_tone3[mark_index + 1:]
number = _get_number_from_pinyin(tone3)
if number is None:
return tone3
s = '{}{}{}'.format(before, number, after)
return _fix_v_u(tone3, s, v_to_u=v_to_u)
def _improve_tone3(tone3, neutral_tone_with_five=False):
number = _get_number_from_pinyin(tone3)
if number is None and neutral_tone_with_five and tone3 != '':
tone3 = '{}5'.format(tone3)
return tone3
def _get_number_from_pinyin(pinyin):
numbers = _re_number.findall(pinyin)
if numbers:
number = numbers[0]
else:
number = None
return number
def _v_to_u(pinyin, replace=False):
if not replace:
return pinyin
return pinyin.replace('v', 'ü')
def _fix_v_u(origin_py, new_py, v_to_u):
if not v_to_u:
return new_py.replace('ü', 'v')
return _v_to_u(new_py, replace=True)
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from typing import Any
from typing import Optional
from typing import Text
from typing import Tuple
_re_number = ... # type: Any
def to_normal(pinyin: Text, v_to_u: bool = ...) -> Text: ...
def to_tone(pinyin: Text) -> Text: ...
def to_tone2(pinyin: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_tone3(pinyin: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_initials(pinyin: Text, strict: bool = ...) -> Text: ...
def to_finals(pinyin: Text, strict: bool = ..., v_to_u: bool = ...) -> Text: ...
def to_finals_tone(pinyin: Text, strict: bool = ...) -> Text: ...
def to_finals_tone2(pinyin: Text, strict: bool = ..., v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def to_finals_tone3(pinyin: Text, strict: bool = ..., v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone_to_normal(tone: Text, v_to_u: bool = ...) -> Text: ...
def tone_to_tone2(tone: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone_to_tone3(tone: Text, v_to_u: bool = ..., neutral_tone_with_five: bool = ...) -> Text: ...
def tone2_to_normal(tone2: Text, v_to_u: bool = ...) -> Text: ...
def tone2_to_tone(tone2: Text) -> Text: ...
def tone2_to_tone3(tone2: Text, v_to_u: bool = ...) -> Text: ...
def tone3_to_normal(tone3: Text, v_to_u: bool = ...) -> Text: ...
def tone3_to_tone(tone3: Text) -> Text: ...
def tone3_to_tone2(tone3: Text, v_to_u: bool = ...) -> Text: ...
def _improve_tone3(tone3: Text, neutral_tone_with_five: bool = ...) -> Text: ...
def _get_number_from_pinyin(pinyin: Text) -> Optional[int]: ...
def _v_to_u(pinyin: Text, replace: bool = ...) -> Text: ...
def _fix_v_u(origin_py: Text, new_py: Text, v_to_u: bool) -> Text: ...
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
"""
标调位置
ɑ 不放过,
  没 ɑ 找 o、e
  ɑ、o、e、i、u、ü
  标调就按这顺序;
  i、u 若是连在一起,
  谁在后面就标谁。
http://www.hwjyw.com/resource/content/2010/06/04/8183.shtml
https://www.zhihu.com/question/23655297
https://github.com/mozillazg/python-pinyin/issues/160
http://www.pinyin.info/rules/where.html
"""
# TODO: 增加测试用例:使用 pinyin_dict 中的数据收集所有带声调拼音测试这个规则的正确性
# 1. 收集所有带声调拼音
# 2. 转换为数字声调拼音(tone3),然后再转换为声调拼音
# 3. 比对转换后的声调拼音跟原始拼音,确保结果一致
def right_mark_index(pinyin_no_tone):
# 'iou', 'uei', 'uen': 还原原始韵母后,还需要按照还原前的声调进行标记
if 'iou' in pinyin_no_tone:
return pinyin_no_tone.index('u')
if 'uei' in pinyin_no_tone:
return pinyin_no_tone.index('i')
if 'uen' in pinyin_no_tone:
return pinyin_no_tone.index('u')
# 有 ɑ 不放过, 没 ɑ 找 o、e
for c in ['a', 'o', 'e']:
if c in pinyin_no_tone:
return pinyin_no_tone.index(c) + len(c) - 1
# i、u 若是连在一起,谁在后面就标谁
for c in ['iu', 'ui']:
if c in pinyin_no_tone:
return pinyin_no_tone.index(c) + len(c) - 1
# ɑ、o、e、i、u、ü
for c in ['i', 'u', 'v', 'ü']:
if c in pinyin_no_tone:
return pinyin_no_tone.index(c) + len(c) - 1
# n, m, ê
for c in ['n', 'm', 'ê']:
if c in pinyin_no_tone:
return pinyin_no_tone.index(c) + len(c) - 1
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from typing import Text, Optional
def right_mark_index(pinyin_no_number: Text) -> Optional[int]: ...
@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from pypinyin.standard import convert_finals
from pypinyin.style._constants import (
_INITIALS, _INITIALS_NOT_STRICT, _FINALS,
RE_PHONETIC_SYMBOL, PHONETIC_SYMBOL_DICT,
PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE,
RE_NUMBER
)
def get_initials(pinyin, strict):
"""获取单个拼音中的声母.
:param pinyin: 单个拼音
:type pinyin: unicode
:param strict: 是否严格遵照《汉语拼音方案》来处理声母和韵母
:return: 声母
:rtype: unicode
"""
if strict:
_initials = _INITIALS
else:
_initials = _INITIALS_NOT_STRICT
for i in _initials:
if pinyin.startswith(i):
return i
return ''
def get_finals(pinyin, strict):
"""获取单个拼音中的韵母.
:param pinyin: 单个拼音,无声调拼音
:type pinyin: unicode
:param strict: 是否严格遵照《汉语拼音方案》来处理声母和韵母
:return: 韵母
:rtype: unicode
"""
if strict:
pinyin = convert_finals(pinyin)
initials = get_initials(pinyin, strict=strict) or ''
# 按声母分割,剩下的就是韵母
finals = pinyin[len(initials):]
# 处理既没有声母也没有韵母的情况
if strict and finals not in _FINALS:
# 处理 y, w 导致误判的问题,比如 yo
initials = get_initials(pinyin, strict=False)
finals = pinyin[len(initials):]
if finals in _FINALS:
return finals
return ''
# ń, ḿ
if not finals and not strict:
return pinyin
return finals
def replace_symbol_to_number(pinyin):
"""把声调替换为数字"""
def _replace(match):
symbol = match.group(0) # 带声调的字符
# 返回使用数字标识声调的字符
return PHONETIC_SYMBOL_DICT[symbol]
# 替换拼音中的带声调字符
value = RE_PHONETIC_SYMBOL.sub(_replace, pinyin)
for symbol, to in PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE.items():
value = value.replace(symbol, to)
return value
def replace_symbol_to_no_symbol(pinyin):
"""把带声调字符替换为没有声调的字符"""
value = replace_symbol_to_number(pinyin)
return RE_NUMBER.sub('', value)
def has_finals(pinyin):
"""判断是否有韵母"""
# 鼻音: 'm̄', 'ḿ', 'm̀', 'ń', 'ň', 'ǹ ' 没有韵母
for symbol in ['', 'ḿ', '', 'ń', 'ň', 'ǹ']:
if symbol in pinyin:
return False
return True
@@ -0,0 +1,15 @@
from typing import Text
def get_initials(pinyin: Text, strict: bool) -> Text: ...
def get_finals(pinyin: Text, strict: bool) -> Text: ...
def replace_symbol_to_number(pinyin: Text) -> Text: ...
def replace_symbol_to_no_symbol(pinyin: Text) -> Text: ...
def has_finals(pinyin: Text) -> bool: ...
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
"""BOPOMOFO 相关的几个拼音风格实现:
Style.BOPOMOFO
Style.BOPOMOFO_FIRST
"""
from __future__ import unicode_literals
import re
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._constants import RE_TONE3
from pypinyin.style._utils import replace_symbol_to_number
# 注音转换表
BOPOMOFO_REPLACE = (
(re.compile(r'^m(\d)$'), 'mu\\1'), # 呣
(re.compile(r'^n(\d)$'), 'N\\1'), # 嗯
(re.compile(r'^r5$'), 'er5'), # 〜兒
(re.compile(r'iu'), 'iou'),
(re.compile(r'ui'), 'uei'),
(re.compile(r'ong'), 'ung'),
(re.compile(r'^yi?'), 'i'),
(re.compile(r'^wu?'), 'u'),
(re.compile(r'iu'), 'v'),
(re.compile(r'^([jqx])u'), '\\1v'),
(re.compile(r'([iuv])n'), '\\1en'),
(re.compile(r'^zhi?'), 'Z'),
(re.compile(r'^chi?'), 'C'),
(re.compile(r'^shi?'), 'S'),
(re.compile(r'^([zcsr])i'), '\\1'),
(re.compile(r'ai'), 'A'),
(re.compile(r'ei'), 'I'),
(re.compile(r'ao'), 'O'),
(re.compile(r'ou'), 'U'),
(re.compile(r'ang'), 'K'),
(re.compile(r'eng'), 'G'),
(re.compile(r'an'), 'M'),
(re.compile(r'en'), 'N'),
(re.compile(r'er'), 'R'),
(re.compile(r'eh'), 'E'),
(re.compile(r'([iv])e'), '\\1E'),
(re.compile(r'([^0-4])$'), '\\g<1>0'),
(re.compile(r'1$'), ''),
)
BOPOMOFO_TABLE = dict(zip(
'bpmfdtnlgkhjqxZCSrzcsiuvaoeEAIOUMNKGR2340ê',
'ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄧㄨㄩㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦˊˇˋ˙ㄝ'
))
class BopomofoConverter(object):
def to_bopomofo(self, pinyin, **kwargs):
pinyin = self._pre_convert(pinyin)
# 查表替换成注音
for find_re, replace in BOPOMOFO_REPLACE:
pinyin = find_re.sub(replace, pinyin)
pinyin = ''.join(BOPOMOFO_TABLE.get(x, x) for x in pinyin)
return pinyin
def to_bopomofo_first(self, pinyin, **kwargs):
pinyin = self.to_bopomofo(pinyin, **kwargs)
return pinyin[0]
def _pre_convert(self, pinyin):
# 用数字表示声调
pinyin = replace_symbol_to_number(pinyin)
# 将声调数字移动到最后
return RE_TONE3.sub(r'\1\3\2', pinyin)
converter = BopomofoConverter()
register(Style.BOPOMOFO, func=converter.to_bopomofo)
register(Style.BOPOMOFO_FIRST, func=converter.to_bopomofo_first)
@@ -0,0 +1,15 @@
from typing import Any, Dict, Tuple, Text
# 注音转换表
BOPOMOFO_REPLACE = ... # type: Tuple[Tuple[Any]]
BOPOMOFO_TABLE = ... # type: Dict[Text, Text]
class BopomofoConverter(object):
def to_bopomofo(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_bopomofo_first(self, pinyin: Text, **kwargs: Any) -> Text: ...
def _pre_convert(self, pinyin: Text) -> Text: ...
converter = ... # type: BopomofoConverter
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
"""中国内地盲文相关的几个拼音风格实现:
Style.BRAILLE_MAINLAND
Style.BRAILLE_MAINLAND_TONE
https://en.wikipedia.org/wiki/Mainland_Chinese_Braille
"""
from __future__ import unicode_literals
import re
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._constants import RE_TONE3
from pypinyin.style._utils import (
replace_symbol_to_number, replace_symbol_to_no_symbol)
BRAILLE_MAINLAND_REPLACE = (
(re.compile(r'iu'), 'iou'),
(re.compile(r'ui'), 'uei'),
(re.compile(r'un'), 'uen'),
(re.compile(r'ong'), 'ung'),
(re.compile(r'^yi?'), 'i'),
(re.compile(r'^wu?'), 'u'),
(re.compile(r'y'), 'i'),
(re.compile(r'w'), 'u'),
(re.compile(r'iu'), 'v'),
(re.compile(r'^([jqx])u'), '\\1v'),
(re.compile(r'([iuv])n'), '\\1en'),
(re.compile(r'^zhi?'), 'Z'),
(re.compile(r'^chi?'), 'C'),
(re.compile(r'^shi?'), 'S'),
(re.compile(r'^([zcsr])i'), '\\1'),
(re.compile(r'iang'), ''),
(re.compile(r'uang'), ''),
(re.compile(r'ueng'), ''),
(re.compile(r'iong'), ''),
(re.compile(r'ang'), ''),
(re.compile(r'eng'), ''),
(re.compile(r'uai'), ''),
(re.compile(r'iao'), ''),
(re.compile(r'iou'), ''),
(re.compile(r'ian'), ''),
(re.compile(r'uan'), ''),
(re.compile(r'van'), ''),
(re.compile(r'uen'), ''),
(re.compile(r'ing'), ''),
(re.compile(r'ong'), ''),
(re.compile(r'er'), ''),
(re.compile(r'ai'), ''),
(re.compile(r'ei'), ''),
(re.compile(r'ao'), ''),
(re.compile(r'ou'), ''),
(re.compile(r'an'), ''),
(re.compile(r'en'), ''),
(re.compile(r'ia'), ''),
(re.compile(r'ua'), ''),
(re.compile(r'ie'), ''),
(re.compile(r'uo'), ''),
(re.compile(r've'), ''),
(re.compile(r'ui'), ''),
(re.compile(r'in'), ''),
(re.compile(r'vn'), ''),
)
BRAILLE_MAINLAND_TABLE = dict(zip(
'bpmfdtnlgkhjqxZCSrzcsiuvaoe1234',
'⠃⠏⠍⠋⠙⠞⠝⠇⠛⠅⠓⠛⠅⠓⠌⠟⠱⠚⠵⠉⠎⠊⠥⠬⠔⠢⠢⠁⠂⠄⠆'
))
class BrailleMainlandConverter(object):
def to_braille_mainland_tone(self, pinyin, **kwargs):
# 用数字表示声调
pinyin = replace_symbol_to_number(pinyin)
# 将声调数字移动到最后
pinyin = RE_TONE3.sub(r'\1\3\2', pinyin)
for find_re, replace in BRAILLE_MAINLAND_REPLACE:
pinyin = find_re.sub(replace, pinyin)
pinyin = ''.join(BRAILLE_MAINLAND_TABLE.get(x, x) for x in pinyin)
return pinyin
def to_braille_mainland(self, pinyin, **kwargs):
pinyin = replace_symbol_to_no_symbol(pinyin)
for find_re, replace in BRAILLE_MAINLAND_REPLACE:
pinyin = find_re.sub(replace, pinyin)
pinyin = ''.join(BRAILLE_MAINLAND_TABLE.get(x, x) for x in pinyin)
return pinyin
converter = BrailleMainlandConverter()
register(Style.BRAILLE_MAINLAND_TONE, func=converter.to_braille_mainland_tone)
register(Style.BRAILLE_MAINLAND, func=converter.to_braille_mainland)
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
"""中国内地盲文相关的几个拼音风格实现:
Style.BRAILLE_MAINLAND
Style.BRAILLE_MAINLAND_TONE
"""
from typing import Any, Dict, Tuple, Text
BRAILLE_MAINLAND_REPLACE = ... # type: Tuple[Tuple[Any]]
BRAILLE_MAINLAND_TABLE = ... # type: Dict[Text, Text]
class BrailleMainlandConverter(object):
def to_braille_mainland_tone(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_braille_mainland(self, pinyin: Text, **kwargs: Any) -> Text: ...
converter = ... # type: BrailleMainlandConverter
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""CYRILLIC 相关的几个拼音风格实现:
Style.CYRILLIC
Style.CYRILLIC_FIRST
"""
from __future__ import unicode_literals
import re
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._constants import RE_TONE3
from pypinyin.style._utils import replace_symbol_to_number
# 俄语转换表
CYRILLIC_REPLACE = (
(re.compile(r'ong'), 'ung'),
(re.compile(r'([zcs])i'), '\\1U'),
(re.compile(r'([xqj])u'), '\\1v'),
(re.compile(r'^wu(.?)$'), 'u\\1'),
(re.compile(r'(.+)r(.?)$'), '\\1R\\2'),
(re.compile(r'^zh'), 'Cr'),
(re.compile(r'^ch'), 'C'),
(re.compile(r'^j'), 'qZ'),
(re.compile(r'^z'), 'qZ'),
(re.compile(r'^x'), 's'),
(re.compile(r'^sh'), 'S'),
(re.compile(r'([^CSdst])uo'), '\\1o'),
(re.compile(r'^y(.*)$'), 'I\\1'),
(re.compile(r'Iai'), 'AI'),
(re.compile(r'Ia'), 'A'),
(re.compile(r'Ie'), 'E'),
(re.compile(r'Ii'), 'i'),
(re.compile(r'Iou'), 'V'),
(re.compile(r'Iu'), 'v'),
(re.compile(r'(.v)(\d?)$'), '\\1I\\2'),
(re.compile(r'Io'), 'O'),
(re.compile(r'iu'), 'v'),
(re.compile(r'ie'), 'E'),
(re.compile(r'hui'), 'huei'),
(re.compile(r'ui'), 'uI'),
(re.compile(r'ai'), 'aI'),
(re.compile(r'ei'), 'eI'),
(re.compile(r'ia'), 'A'),
(re.compile(r'(.*[^h])n([^g]?)$'), '\\1nM\\2'),
(re.compile(r'(.*[^h])ng(.?)$'), '\\1n\\2'),
(re.compile(r'^v(\d?$)'), 'vI'),
)
CYRILLIC_TABLE = dict(zip(
u'abwgdEOrZiIklmnopRstufhqcCSHTMUevAV',
u'абвгдеёжзийклмнопрстуфхццчшщъьыэюяю'
))
class CyrillicfoConverter(object):
def to_cyrillic(self, pinyin, **kwargs):
pinyin = self._pre_convert(pinyin)
# 查表替换成注音
for find_re, replace in CYRILLIC_REPLACE:
pinyin = find_re.sub(replace, pinyin)
pinyin = ''.join(CYRILLIC_TABLE.get(x, x) for x in pinyin)
return pinyin
def to_cyrillic_first(self, pinyin, **kwargs):
pinyin = self.to_cyrillic(pinyin, **kwargs)
return pinyin[0]
def _pre_convert(self, pinyin):
# 用数字表示声调
pinyin = replace_symbol_to_number(pinyin)
# 将声调数字移动到最后
return RE_TONE3.sub(r'\1\3\2', pinyin)
converter = CyrillicfoConverter()
register(Style.CYRILLIC, func=converter.to_cyrillic)
register(Style.CYRILLIC_FIRST, func=converter.to_cyrillic_first)
@@ -0,0 +1,16 @@
from typing import Any, Dict, Tuple, Text
# 俄语转换表
CYRILLIC_REPLACE = ... # type: Tuple[Tuple[Any]]
CYRILLIC_TABLE = ... # type: Dict[Text, Text]
class CyrillicfoConverter(object):
def to_cyrillic(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_cyrillic_first(self, pinyin: Text, **kwargs: Any) -> Text: ...
def _pre_convert(self, pinyin: Text) -> Text: ...
converter = ... # type: CyrillicfoConverter
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
"""韵母相关拼音风格:
Style.FINALS
Style.FINALS_TONE
Style.FINALS_TONE2
Style.FINALS_TONE3
"""
from __future__ import unicode_literals
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._tone_convert import (
to_finals, to_finals_tone, to_finals_tone2, to_finals_tone3
)
class FinalsConverter(object):
def to_finals(self, pinyin, **kwargs):
"""无声调韵母"""
return to_finals(pinyin, strict=kwargs.get('strict', True))
def to_finals_tone(self, pinyin, **kwargs):
"""声调在韵母头上"""
return to_finals_tone(pinyin, strict=kwargs.get('strict', True))
def to_finals_tone2(self, pinyin, **kwargs):
"""数字声调"""
return to_finals_tone2(pinyin, strict=kwargs.get('strict', True))
def to_finals_tone3(self, pinyin, **kwargs):
"""数字声调"""
return to_finals_tone3(pinyin, strict=kwargs.get('strict', True))
converter = FinalsConverter()
register(Style.FINALS, func=converter.to_finals)
register(Style.FINALS_TONE, func=converter.to_finals_tone)
register(Style.FINALS_TONE2, func=converter.to_finals_tone2)
register(Style.FINALS_TONE3, func=converter.to_finals_tone3)
@@ -0,0 +1,14 @@
from typing import Any, Text
class FinalsConverter(object):
def to_finals(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_finals_tone(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_finals_tone2(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_finals_tone3(self, pinyin: Text, **kwargs: Any) -> Text: ...
converter = ... # type: FinalsConverter
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._constants import RE_TONE3
from pypinyin.style._utils import replace_symbol_to_number
GWOYEU_REPLACE = (
(re.compile(r'^r5$'), 'er5'),
(re.compile(r'iu'), 'iou'),
(re.compile(r'ao'), 'au'),
(re.compile(r'^yi?'), 'i'),
(re.compile(r'^wu?'), 'u'),
(re.compile(r'^([jqx])u'), '\\1iu'),
(re.compile(r'(?<![iy])u([in])'), 'ue\\1'),
(re.compile(r'v'), 'iu'),
(re.compile(r'^([zcsr]h?)i'), '\\1y'),
(re.compile(r'^zh'), 'j'),
(re.compile(r'^z'), 'tz'),
(re.compile(r'^c(?!h)'), 'ts'),
(re.compile(r'^q'), 'ch'),
(re.compile(r'^x'), 'sh'),
(re.compile(r'er'), 'el'),
(re.compile(r'5$'), ''),
(re.compile(r'0$'), 'q'),
(re.compile(r'^i(.*[34])$'), 'yi\\1'),
(re.compile(r'^u(.*[34])$'), 'wu\\1'),
(re.compile(r'^yi([aeu].*4)$'), 'y\\1'),
(re.compile(r'^wu([ae].*4)$'), 'w\\1'),
)
TONE_REPLACE = (
(re.compile(r'^([lmnr])(.+)1$'), '\\1h\\2'),
(re.compile(r'1$'), ''),
(re.compile(r'^([lmnr])(.+)2$'), '\\1\\2'),
(re.compile(r'^([^ae]*)i(ng?)*2$'), '\\1yi\\2'),
(re.compile(r'^([^ao]*)u2$'), '\\1wu'),
(re.compile(r'^([^ae]*)i(.+)2$'), '\\1y\\2'),
(re.compile(r'^([^ao]*)u(.+)2$'), '\\1w\\2'),
(re.compile(r'([aeiouy]+)(.*)2$'), '\\1r\\2'),
(re.compile(r'^([^aeiou]*)([iu])(ng?)?3$'), '\\1\\2\\2\\3'),
(re.compile(r'^([^eu]*)i(.*)3$'), '\\1e\\2'),
(re.compile(r'^(.*)u(.*)3$'), '\\1o\\2'),
(re.compile(r'([aeiouy])(.*)3$'), '\\1\\1\\2'),
(re.compile(r'^([^ae]*)i4$'), '\\1ih'),
(re.compile(r'^([^ao]*)u4$'), '\\1uh'),
(re.compile(r'i4$'), 'y'),
(re.compile(r'u4$'), 'w'),
(re.compile(r'l4$'), 'll'),
(re.compile(r'ng4$'), 'nq'),
(re.compile(r'n4$'), 'nn'),
(re.compile(r'4$'), 'h'),
)
class GwoyeuConverter(object):
def _pre_convert(self, pinyin):
# 用数字表示声调
pinyin = replace_symbol_to_number(pinyin)
# 将声调数字移动到最后
return RE_TONE3.sub(r'\1\3\2', pinyin)
def to_gwoyeu(self, pinyin, **kwargs):
pinyin = self._pre_convert(pinyin)
for find_re, replace in GWOYEU_REPLACE:
pinyin = find_re.sub(replace, pinyin)
for find_re, replace in TONE_REPLACE:
if find_re.search(pinyin):
return find_re.sub(replace, pinyin)
return pinyin
converter = GwoyeuConverter()
register(Style.GWOYEU, func=converter.to_gwoyeu)
@@ -0,0 +1,10 @@
from typing import Any, Text, Tuple
GWOYEU_REPLACE = ... # type: Tuple[Tuple[Any]]
class GwoyeuConverter(object):
def to_wade_glides(self, pinyin: Text, **kwargs: Any) -> Text: ...
def _pre_convert(self, pinyin: Text) -> Text: ...
converter = ... # type: GwoyeuConverter
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
"""Style.INITIALS 风格"""
from __future__ import unicode_literals
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._utils import get_initials
@register(Style.INITIALS)
def convert(pinyin, **kwargs):
strict = kwargs.get('strict', True)
return get_initials(pinyin, strict)
@@ -0,0 +1,4 @@
from typing import Any, Text
def convert(pinyin: Text, **kwargs: Any) -> Text: ...
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
"""其他几个拼音风格实现:
Style.NORMAL
Style.FIRST_LETTER
"""
from __future__ import unicode_literals
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._utils import replace_symbol_to_no_symbol
class OthersConverter(object):
def to_normal(self, pinyin, **kwargs):
pinyin = replace_symbol_to_no_symbol(pinyin)
return pinyin
def to_first_letter(self, pinyin, **kwargs):
# 用数字表示声调
pinyin = self.to_normal(pinyin)
return pinyin[0]
converter = OthersConverter()
register(Style.NORMAL, func=converter.to_normal)
register(Style.FIRST_LETTER, func=converter.to_first_letter)
@@ -0,0 +1,10 @@
from typing import Any, Text
class OthersConverter(object):
def to_normal(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_first_letter(self, pinyin: Text, **kwargs: Any) -> Text: ...
converter = ... # type: OthersConverter
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
"""TONE 相关的几个拼音风格实现:
Style.TONE
Style.TONE2
Style.TONE3
"""
from __future__ import unicode_literals
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._constants import RE_TONE3
from pypinyin.style._utils import replace_symbol_to_number
class ToneConverter(object):
def to_tone(self, pinyin, **kwargs):
return pinyin
def to_tone2(self, pinyin, **kwargs):
# 用数字表示声调
pinyin = replace_symbol_to_number(pinyin)
return pinyin
def to_tone3(self, pinyin, **kwargs):
pinyin = self.to_tone2(pinyin, **kwargs)
# 将声调数字移动到最后
return RE_TONE3.sub(r'\1\3\2', pinyin)
converter = ToneConverter()
register(Style.TONE, func=converter.to_tone)
register(Style.TONE2, func=converter.to_tone2)
register(Style.TONE3, func=converter.to_tone3)
@@ -0,0 +1,12 @@
from typing import Any, Text
class ToneConverter(object):
def to_tone(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_tone2(self, pinyin: Text, **kwargs: Any) -> Text: ...
def to_tone3(self, pinyin: Text, **kwargs: Any) -> Text: ...
converter = ... # type: ToneConverter
@@ -0,0 +1,559 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from pypinyin.constants import Style
from pypinyin.style import register
from pypinyin.style._utils import replace_symbol_to_no_symbol
from pypinyin.style._utils import get_initials
# https://en.wikipedia.org/wiki/Wade%E2%80%93Giles
# https://web.archive.org/web/20070128065433/http://polaris.gseis.ucla.edu/skozerow/wadegiles.htm
# https://www.lib.uchicago.edu/about/directory/departments/eastasia/find/wade-giles-pinyin-conversion-table/
# https://www.zdic.net/ts/fulu/2019/05/18.html
_convert_table = [
['a', 'a'],
['ai', 'ai'],
['an', 'an'],
['ang', 'ang'],
['ao', 'ao'],
['zha', 'cha'],
['cha', 'ch\'a'],
['zhai', 'chai'],
['chai', 'ch\'ai'],
['zhan', 'chan'],
['chan', 'ch\'an'],
['zhang', 'chang'],
['chang', 'ch\'ang'],
['zhao', 'chao'],
['chao', 'ch\'ao'],
['zhe', 'che'],
['che', 'ch\'e'],
['zhen', 'chen'],
['chen', 'ch\'en'],
['zheng', 'cheng'],
['cheng', 'ch\'eng'],
['ji', 'chi'],
['qi', 'ch\'i'],
['jia', 'chia'],
['qia', 'ch\'ia'],
['jiang', 'chiang'],
['qiang', 'ch\'iang'],
['jiao', 'chiao'],
['qiao', 'ch\'iao'],
['jie', 'chieh'],
['qie', 'ch\'ieh'],
['jian', 'chien'],
['qian', 'ch\'ien'],
['zhi', 'chih'],
['chi', 'ch\'ih'],
['jin', 'chin'],
['qin', 'ch\'in'],
['jing', 'ching'],
['qing', 'ch\'ing'],
['jiu', 'chiu'],
['qiu', 'ch\'iu'],
['jiong', 'chiung'],
['qiong', 'ch\'iung'],
['zhuo', 'cho'],
['chuo', 'ch\'o'],
['zhou', 'chou'],
['chou', 'ch\'ou'],
['zhu', 'chu'],
['chu', 'ch\'u'],
['ju', 'chü'],
['qu', 'ch\'ü'],
['zhua', 'chua'],
['zhuai', 'chuai'],
['chuai', 'ch\'uai'],
['zhuan', 'chuan'],
['chuan', 'ch\'uan'],
['juan', 'chüan'],
['quan', 'ch\'üan'],
['zhuang', 'chuang'],
['chuang', 'ch\'uang'],
['jue', 'chüeh'],
['que', 'ch\'üeh'],
['zhui', 'chui'],
['chui', 'ch\'ui'],
['zhun', 'chun'],
['chun', 'ch\'un'],
['jun', 'chün'],
['qun', 'ch\'ün'],
['zhong', 'chung'],
['chong', 'ch\'ung'],
['en', 'en'],
['er', 'erh'],
['fa', 'fa'],
['fan', 'fan'],
['fang', 'fang'],
['fei', 'fei'],
['fen', 'fen'],
['feng', 'feng'],
['fo', 'fo'],
['fou', 'fou'],
['fu', 'fu'],
['ha', 'ha'],
['hai', 'hai'],
['han', 'han'],
['hang', 'hang'],
['hao', 'hao'],
['hei', 'hei'],
['hen', 'hen'],
['heng', 'heng'],
['he', 'ho'],
['hou', 'hou'],
['xi', 'hsi'],
['xia', 'hsia'],
['xiang', 'hsiang'],
['xiao', 'hsiao'],
['xie', 'hsieh'],
['xian', 'hsien'],
['xin', 'hsin'],
['xing', 'hsing'],
['xiu', 'hsiu'],
['xiong', 'hsiung'],
['xu', 'hsü'],
['xuan', 'hsüan'],
['xue', 'hsüeh'],
['xun', 'hsün'],
['hu', 'hu'],
['hua', 'hua'],
['huai', 'huai'],
['huan', 'huan'],
['huang', 'huang'],
['hui', 'hui'],
['hun', 'hun'],
['hong', 'hung'],
['huo', 'huo'],
['yi', 'i'],
['ran', 'jan'],
['rang', 'jang'],
['rao', 'jao'],
['re', 'je'],
['ren', 'jen'],
['reng', 'jeng'],
['ri', 'jih'],
['ruo', 'jo'],
['rou', 'jou'],
['ru', 'ju'],
['ruan', 'juan'],
['rui', 'jui'],
['run', 'jun'],
['rong', 'jung'],
['ga', 'ka'],
['ka', 'k\'a'],
['gai', 'kai'],
['kai', 'k\'ai'],
['gan', 'kan'],
['kan', 'k\'an'],
['gang', 'kang'],
['kang', 'k\'ang'],
['gao', 'kao'],
['kao', 'k\'ao'],
['gen', 'ken'],
['ken', 'k\'en'],
['geng', 'keng'],
['keng', 'k\'eng'],
['ge', 'ko'],
['ke', 'k\'o'],
['gou', 'kou'],
['kou', 'k\'ou'],
['gu', 'ku'],
['ku', 'k\'u'],
['gua', 'kua'],
['kua', 'k\'ua'],
['guai', 'kuai'],
['kuai', 'k\'uai'],
['guan', 'kuan'],
['kuan', 'k\'uan'],
['guang', 'kuang'],
['kuang', 'k\'uang'],
['gui', 'kuei'],
['kui', 'k\'uei'],
['gun', 'kun'],
['kun', 'k\'un'],
['gong', 'kung'],
['kong', 'k\'ung'],
['guo', 'kuo'],
['kuo', 'k\'uo'],
['la', 'la'],
['lai', 'lai'],
['lan', 'lan'],
['lang', 'lang'],
['lao', 'lao'],
['le', 'le'],
['lei', 'lei'],
['leng', 'leng'],
['li', 'li'],
['liang', 'liang'],
['liao', 'liao'],
['lie', 'lieh'],
['lian', 'lien'],
['lin', 'lin'],
['ling', 'ling'],
['liu', 'liu'],
['luo', 'lo'],
['lou', 'lou'],
['lu', 'lu'],
['', ''],
['luan', 'luan'],
['luan', 'lüan'],
['lue', 'lüeh'],
['lun', 'lun'],
['long', 'lung'],
['ma', 'ma'],
['mai', 'mai'],
['man', 'man'],
['mang', 'mang'],
['mao', 'mao'],
['mei', 'mei'],
['men', 'men'],
['meng', 'meng'],
['mi', 'mi'],
['miao', 'miao'],
['mie', 'mieh'],
['mian', 'mien'],
['min', 'min'],
['ming', 'ming'],
['miu', 'miu'],
['mo', 'mo'],
['mou', 'mou'],
['mu', 'mu'],
['na', 'na'],
['nai', 'nai'],
['nan', 'nan'],
['nang', 'nang'],
['nao', 'nao'],
['nei', 'nei'],
['nen', 'nen'],
['neng', 'neng'],
['ni', 'ni'],
['niang', 'niang'],
['niao', 'niao'],
['nie', 'nieh'],
['nian', 'nien'],
['nin', 'nin'],
['ning', 'ning'],
['niu', 'niu'],
['nuo', 'no'],
['nou', 'nou'],
['nu', 'nu'],
['', ''],
['nuan', 'nuan'],
['nue', 'nüeh'],
['nong', 'nung'],
['e', 'o'],
['ou', 'ou'],
['ba', 'pa'],
['pa', 'p\'a'],
['bai', 'pai'],
['pai', 'p\'ai'],
['ban', 'pan'],
['pan', 'p\'an'],
['bang', 'pang'],
['pang', 'p\'ang'],
['bao', 'pao'],
['pao', 'p\'ao'],
['bei', 'pei'],
['pei', 'p\'ei'],
['ben', 'pen'],
['pen', 'p\'en'],
['beng', 'peng'],
['peng', 'p\'eng'],
['bi', 'pi'],
['pi', 'p\'i'],
['biao', 'piao'],
['piao', 'p\'iao'],
['bie', 'pieh'],
['pie', 'p\'ieh'],
['bian', 'pien'],
['pian', 'p\'ien'],
['bin', 'pin'],
['pin', 'p\'in'],
['bing', 'ping'],
['ping', 'p\'ing'],
['bo', 'po'],
['po', 'p\'o'],
['pou', 'p\'ou'],
['bu', 'pu'],
['pu', 'p\'u'],
['sa', 'sa'],
['sai', 'sai'],
['san', 'san'],
['sang', 'sang'],
['sao', 'sao'],
['se', 'se'],
['sen', 'sen'],
['seng', 'seng'],
['sha', 'sha'],
['shai', 'shai'],
['shan', 'shan'],
['shang', 'shang'],
['shao', 'shao'],
['she', 'she'],
['shen', 'shen'],
['sheng', 'sheng'],
['shi', 'shih'],
['shou', 'shou'],
['shu', 'shu'],
['shua', 'shua'],
['shuai', 'shuai'],
['shuan', 'shuan'],
['shuang', 'shuang'],
['shui', 'shui'],
['shun', 'shun'],
['shuo', 'shuo'],
['suo', 'so'],
['sou', 'sou'],
['si', 'ssu'],
['su', 'su'],
['suan', 'suan'],
['sui', 'sui'],
['sun', 'sun'],
['song', 'sung'],
['da', 'ta'],
['ta', 't\'a'],
['dai', 'tai'],
['tai', 't\'ai'],
['dan', 'tan'],
['tan', 't\'an'],
['dang', 'tang'],
['tang', 't\'ang'],
['dao', 'tao'],
['tao', 't\'ao'],
['de', 'te'],
['te', 't\'e'],
['deng', 'teng'],
['teng', 't\'eng'],
['di', 'ti'],
['ti', 't\'i'],
['diao', 'tiao'],
['tiao', 't\'iao'],
['die', 'tieh'],
['tie', 't\'oeh'],
['dian', 'tien'],
['tian', 't\'ien'],
['ding', 'ting'],
['ting', 't\'ing'],
['diu', 'tiu'],
['duo', 'to'],
['tuo', 't\'o'],
['dou', 'tou'],
['tou', 't\'ou'],
['du', 'tu'],
['tu', 't\'u'],
['duan', 'tuan'],
['tuan', 't\'uan'],
['dui', 'tui'],
['tui', 't\'ui'],
['dun', 'tun'],
['tun', 't\'un'],
['dong', 'tung'],
['tong', 't\'ung'],
['za', 'tsa'],
['ca', 'ts\'a'],
['zai', 'tsai'],
['cai', 'ts\'ai'],
['zan', 'tsan'],
['can', 'ts\'an'],
['zang', 'tsang'],
['cang', 'ts\'ang'],
['zao', 'tsao'],
['cao', 'ts\'ao'],
['ze', 'tse'],
['ce', 'ts\'e'],
['zei', 'tsei'],
['zen', 'tsen'],
['cen', 'ts\'en'],
['zeng', 'tseng'],
['ceng', 'ts\'eng'],
['zuo', 'tso'],
['cuo', 'ts\'o'],
['zou', 'tsou'],
['cou', 'ts\'ou'],
['zu', 'tsu'],
['cu', 'ts\'u'],
['zuan', 'tsuan'],
['cuan', 'ts\'uan'],
['zui', 'tsui'],
['cui', 'ts\'ui'],
['zun', 'tsun'],
['cun', 'ts\'un'],
['zong', 'tsung'],
['cong', 'ts\'ung'],
['zi', 'tzu'],
['ci', 'tz\'u'],
['wa', 'wa'],
['wai', 'wai'],
['wan', 'wan'],
['wang', 'wang'],
['wei', 'wei'],
['wen', 'wen'],
['weng', 'weng'],
['wo', 'wo'],
['wu', 'wu'],
['ya', 'ya'],
['yai', 'yai'],
['yang', 'yang'],
['yao', 'yao'],
['ye', 'yeh'],
['yan', 'yen'],
['yin', 'yin'],
['ying', 'ying'],
['yo', 'yo'],
['you', 'yu'],
['yu', ''],
['yuan', 'yüan'],
['yue', 'yüeh'],
['yun', 'yün'],
['yong', 'yung'],
['chua', 'ch`ua'],
['dei', 'tei'],
['den', 'ten'],
['diang', 'tiang'],
['ei', 'ei'],
['eng', 'eng'],
['gei', 'kei'],
['lia', 'lia'],
['lo', 'lo'],
['lüan', 'lüan'],
['lvan', 'lüan'],
['lüe', 'lüeh'],
['lve', 'lüeh'],
['lün', 'lün'],
['lvn', 'lün'],
['me', 'me'],
['ne', 'ne'],
['nia', 'nia'],
['nun', 'nun'],
['nüe', 'nüeh'],
['nve', 'nüeh'],
['o', 'o'],
['sei', 'sei'],
['shei', 'shei'],
['shong', 'shung'],
['zhei', 'chei'],
]
_convert_table.sort(key=lambda x: len(x[0]), reverse=True)
# https://www.mysmth.net/nForum/#!article/Linguistics/4991
_initial_table = [
['b', 'p'],
['p', 'p\''],
['m', 'm'],
['f', 'f'],
['d', 't'],
['t', 't\''],
['n', 'n'],
['l', 'l'],
['g', 'g'],
['k', 'k\''],
['h', 'h'],
['j', 'ch'],
['q', 'ch\''],
['x', 'hs'],
['zh', 'ch'],
['ch', 'ch\''],
['sh', 'sh'],
['r', 'j'],
['z', 'ts'],
['c', 'ts\''],
['s', 's'],
]
_initial_table.sort(key=lambda x: len(x[0]), reverse=True)
_tone_table = [
['i', 'i'],
['u', 'u'],
['ü', 'ü'],
['v', 'ü'],
['a', 'a'],
['ia', 'ia'],
['ua', 'ua'],
['o', 'o'],
['uo', 'o'],
['e', 'e'],
['ie', 'ieh'],
['üe', 'üeh'],
['ve', 'üeh'],
['ai', 'ei'],
['uei', 'ui'],
['ao', 'ao'],
['iao', 'iao'],
['ou', 'ou'],
['iou', 'iu'],
['an', 'an'],
['ian', 'ien'],
['uan', 'uan'],
['üan', 'üan'],
['van', 'üan'],
['en', 'en'],
['in', 'in'],
['uen', 'un'],
['ün', 'ün'],
['vn', 'ün'],
['ang', 'ang'],
['iang', 'iang'],
['uang', 'uang'],
['eng', 'eng'],
['ing', 'ing'],
['ueng', 'ueng'],
['ong', 'ung'],
['iong', 'iung'],
['ê', 'eh'],
['er', 'erh'],
]
_tone_table.sort(key=lambda x: len(x[0]), reverse=True)
_except_table = [
['zhi', 'chih'],
['chi', 'ch\'ih'],
['shi', 'shih'],
['ri', 'jih'],
['zi', 'tzu'],
['ci', 'tz\'u'],
['si', 'ssu'],
['guo', 'guo'],
['kuo', 'k\'uo'],
['huo', 'huo'],
['luo', 'luo'],
['jue', 'chüeh'],
['que', 'ch\'üeh'],
['xue', 'hsüeh'],
]
_except_table.sort(key=lambda x: len(x[0]), reverse=True)
def to_wade_glides(pinyin, **kwargs):
pinyin = replace_symbol_to_no_symbol(pinyin).replace('v', 'ü')
whole_converted = _convert_whole(pinyin, _except_table)
if whole_converted != pinyin:
return _fixed_result(whole_converted)
whole_converted = _convert_whole(pinyin, _convert_table)
if whole_converted != pinyin:
return _fixed_result(whole_converted)
initials = get_initials(pinyin, strict=False)
tones = pinyin[len(initials):]
initials = _convert_whole(initials, _initial_table)
tones = _convert_whole(tones, _tone_table)
return _fixed_result('{}{}'.format(initials, tones))
def _fixed_result(pinyin):
return pinyin.replace('ü', 'v')
def _convert_whole(chars, table):
for pair in table:
f, r = pair
if f == chars:
return r
return chars
register(Style.WADEGILES, func=to_wade_glides)
@@ -0,0 +1,12 @@
from typing import Any, List, Dict, Text
_convert_table = ... # type: List[List[Text]]
_initial_table = ... # type: List[List[Text]]
_tone_table = ... # type: List[List[Text]]
_except_table = ... # type: List[List[Text]]
def to_wade_glides(pinyin: Text, **kwargs: Any) -> Text: ...
@@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from argparse import ArgumentParser
from functools import partial
import re
import sys
from pypinyin.compat import PY2
from pypinyin.style._constants import (
PHONETIC_SYMBOL_DICT, PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE
)
from pypinyin.contrib.tone_convert import (
to_normal,
to_tone,
to_tone2,
to_tone3,
# to_initials,
# to_finals,
# to_finals_tone,
# to_finals_tone2,
# to_finals_tone3,
)
re_pinyin = re.compile(
r'(?m)(^|\s|,)([1-5a-zêü{0}]+)'.format(
re.escape(
''.join(x for x in PHONETIC_SYMBOL_DICT if len(x) == 1)
)
)
)
ACTIONS = {
'to_normal': to_normal,
'to_tone': to_tone,
'to_tone2': to_tone2,
'to_tone3': to_tone3,
# 'to_initials': to_initials,
# 'to_finals': to_finals,
# 'to_finals_tone': to_finals_tone,
# 'to_finals_tone2': to_finals_tone2,
# 'to_finals_tone3': to_finals_tone3,
}
def re_sub(action, match_obj):
func = ACTIONS[action]
converted = func(match_obj.group(2))
return '{0}{1}'.format(match_obj.group(1), converted)
def prepare(input):
for k, v in PHONETIC_SYMBOL_DICT_KEY_LENGTH_NOT_ONE.items():
if k in input:
input = input.replace(k, v)
return input
def convert(action, args):
inputs = args.inputs
for item in inputs:
item = prepare(item)
result = re_pinyin.sub(lambda m: re_sub(action, m), item)
print(result)
def get_parser():
parser = ArgumentParser()
if PY2 or sys.version_info < (3, 7):
subparser = parser.add_subparsers()
else:
subparser = parser.add_subparsers(required=True, title='subcommands')
for key in ACTIONS.keys():
name = key.replace('_', '-')
func = partial(convert, key)
p = subparser.add_parser(
name,
help='call pypinyin.contrib.tone_convert.{}() with inputs'.format(key))
p.set_defaults(func=func)
p.add_argument('inputs', nargs='+')
return parser
def main(argv):
argv = argv[:]
if not sys.stdin.isatty():
pipe_data = sys.stdin.read().strip()
else:
pipe_data = ''
if pipe_data:
argv.append(pipe_data)
parser = get_parser()
args = parser.parse_args(argv)
args.func(args)
if __name__ == '__main__':
main(sys.argv[1:])
@@ -0,0 +1,16 @@
from argparse import ArgumentParser, Namespace
import re
from typing import Union, Text, ByteString, Dict, Any, List
re_pinyin = ... # type: Any
ACTIONS = ... # type: Dict[Text, Any]
def re_sub(action: Text, match_obj: re.Match[Text]) -> Text: ...
def prepare(input: Text) -> Text: ...
def convert(action: Text, args: Namespace) -> None: ...
def get_parser() -> ArgumentParser: ...
def main(argv: List[Text]) -> None: ...
@@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# 用于向后兼容,TODO: 废弃
from pypinyin.seg.simpleseg import simple_seg # noqa
from pypinyin.style._tone_convert import tone2_to_tone
# 用于向后兼容,TODO: 废弃
def _replace_tone2_style_dict_to_default(string):
return tone2_to_tone(string)
def _remove_dup_items(lst, remove_empty=False):
new_lst = []
for item in lst:
if remove_empty and not item:
continue
if item not in new_lst:
new_lst.append(item)
return new_lst
def _remove_dup_and_empty(lst_list):
new_lst_list = []
for lst in lst_list:
lst = _remove_dup_items(lst, remove_empty=True)
if lst:
new_lst_list.append(lst)
else:
new_lst_list.append([''])
return new_lst_list
@@ -0,0 +1,10 @@
from typing import List, Text
def _replace_tone2_style_dict_to_default(string: Text) -> Text: ...
def _remove_dup_items(lst: List[Text]) -> List[Text]: ...
def _remove_dup_and_empty(lst_list: List[List[Text]]) -> List[List[Text]]: ...