xChar

摘要:知其然更要知其所以然。

平片假名转换

结论

片假名转平假名

def convert_kata_to_hira(input_text):
    process_texts = []
    for gana in input_text:
        if 12448 <= ord(gana) <= 12534:  # Match katakana characters
            hira = chr(ord(gana) - 96)  # Convert to hiragana
            process_texts.append(hira)
    output_text = "".join(process_texts)
    return output_text

平假名转片假名

def convert_hira_to_kata(input_text):
    process_texts = []
    for gana in input_text:
        if 12353 <= ord(gana) <= 12438:  # Match hiragana characters
            hira = chr(ord(gana) + 96)  # Convert to katakana
            process_texts.append(hira)
    output_text = "".join(process_texts)
    return output_text

平假名 Unicode

按 Unicode 组织提供的 Unicode 标准文件 U3040.pdf ,平假名的十进制范围是 12353-12447,十六进制范围是[\u3041-\u309f](正则表达式的字符转义使用的是十六进制)。

CharacterDecimalHexadecimal
123533041
123543042
123553043
123563044
123573045
123583046
123593047
123603048
123613049
12362304A
12363304B
12364304C
12365304D
12366304E
12367304F
123683050
123693051
123703052
123713053
123723054
123733055
123743056
123753057
123763058
123773059
12378305A
12379305B
12380305C
12381305D
12382305E
12383305F
123843060
123853061
123863062
123873063
123883064
123893065
123903066
123913067
123923068
123933069
12394306A
12395306B
12396306C
12397306D
12398306E
12399306F
124003070
124013071
124023072
124033073
124043074
124053075
124063076
124073077
124083078
124093079
12410307A
12411307B
12412307C
12413307D
12414307E
12415307F
124163080
124173081
124183082
124193083
124203084
124213085
124223086
124233087
124243088
124253089
12426308A
12427308B
12428308C
12429308D
12430308E
12431308F
124323090
124333091
124343092
124353093
124363094
124373095
124383096
124393097
124403098
124413099
12442309A
12443309B
12444309C
12445309D
12446309E
12447309F

片假名 Unicode

U30A0.pdf,十六进制的范围是[\u30a0-\u30ff],十进制范围是 12448-12543。

CharacterDecimalHexadecimal
1244830A0
1244930A1
1245030A2
1245130A3
1245230A4
1245330A5
1245430A6
1245530A7
1245630A8
1245730A9
1245830AA
1245930AB
1246030AC
1246130AD
1246230AE
1246330AF
1246430B0
1246530B1
1246630B2
1246730B3
1246830B4
1246930B5
1247030B6
1247130B7
1247230B8
1247330B9
1247430BA
1247530BB
1247630BC
1247730BD
1247830BE
1247930BF
1248030C0
1248130C1
1248230C2
1248330C3
1248430C4
1248530C5
1248630C6
1248730C7
1248830C8
1248930C9
1249030CA
1249130CB
1249230CC
1249330CD
1249430CE
1249530CF
1249630D0
1249730D1
1249830D2
1249930D3
1250030D4
1250130D5
1250230D6
1250330D7
1250430D8
1250530D9
1250630DA
1250730DB
1250830DC
1250930DD
1251030DE
1251130DF
1251230E0
1251330E1
1251430E2
1251530E3
1251630E4
1251730E5
1251830E6
1251930E7
1252030E8
1252130E9
1252230EA
1252330EB
1252430EC
1252530ED
1252630EE
1252730EF
1252830F0
1252930F1
1253030F2
1253130F3
1253230F4
1253330F5
1253430F6
1253530F7
1253630F8
1253730F9
1253830FA
1253930FB
1254030FC
1254130FD
1254230FE
1254330FF

片假名语音扩展 Unicode

U31F0.pdf ,十进制范围:12784-12799,十六进制范围是[\u31f0-\u31ff]

P.S. 按 Unicode 官方的说明,这部分的假名用于「阿伊努语」,但我看不出这部分和上面的区别 233

CharacterDecimalHexadecimal
1278431F0
1278531F1
1278631F2
1278731F3
1278831F4
1278931F5
1279031F6
1279131F7
1279231F8
1279331F9
1279431FA
1279531FB
1279631FC
1279731FD
1279831FE
1279931FF

全半角假名转换

半角字符 Unicode

UFF00.pdf,日语半角字符的范围是 65381-65439,十六进制范围:[\uff65-\uff9f]

CharacterDecimalHexadecimal
65381FF65
65382FF66
65383FF67
65384FF68
65385FF69
65386FF6A
65387FF6B
65388FF6C
65389FF6D
65390FF6E
65391FF6F
65392FF70
65393FF71
65394FF72
65395FF73
65396FF74
65397FF75
65398FF76
65399FF77
65400FF78
65401FF79
65402FF7A
65403FF7B
65404FF7C
65405FF7D
65406FF7E
ソ65407FF7F
65408FF80
65409FF81
65410FF82
65411FF83
65412FF84
65413FF85
65414FF86
65415FF87
65416FF88
65417FF89
65418FF8A
65419FF8B
65420FF8C
65421FF8D
65422FF8E
65423FF8F
65424FF90
65425FF91
65426FF92
65427FF93
65428FF94
65429FF95
65430FF96
65431FF97
65432FF98
65433FF99
65434FF9A
65435FF9B
65436FF9C
65437FF9D
65438FF9E
65439FF9F

注:第一个字符是半角的

观察上面的表格,可以注意到下面 2 点:

  1. 平假名没有半角假名(这一点可以和维基百科的半形假名相印证)
  2. 有部分平假名的半角假名是由 2 个字符构成,所以不可能像平片假名转换那样用hira = chr(int(ord(gana) - 96))轻松转换,而是要用字符串替换的方式。

所以,全半角假名的转换比平片假名的转换麻烦得多,个人建议使用第三方库。

mojimoji

如果想从全角转为半角,那么 mojimoji 可能是唯一的选择了。

import mojimoji
print mojimoji.zen_to_han(u'アイウabc012')
# アイウabc012
print mojimoji.zen_to_han(u'アイウabc012', kana=False)
# アイウabc012
print mojimoji.zen_to_han(u'アイウabc012', digit=False)
# アイウabc012
print mojimoji.zen_to_han(u'アイウabc012', ascii=False)
# アイウabc012

print mojimoji.han_to_zen(u'アイウabc012')
# アイウabc012
print mojimoji.han_to_zen(u'アイウabc012', kana=False)
# アイウabc012
print mojimoji.han_to_zen(u'アイウabc012', digit=False)
# アイウabc012
print mojimoji.han_to_zen(u'アイウabc012', ascii=False)
# アイウabc012

另外,按Pythonで半角・全角の変換を高速に行う提供的测评来看,mojimoji 也是速度最快的。

%%time
import mojimoji
import zenhan
import jctconv

s = u'アイオエオ012345' * 10

%time for n in range(1000000): mojimoji.zen_to_han(s)
>>> CPU times: user 3.90 s, sys: 0.03 s, total: 3.93 s Wall time: 3.97 s

%time for n in range(1000000): zenhan.z2h(s)
>>> CPU times: user 71.05 s, sys: 0.16 s, total: 71.22 s Wall time: 71.45 s

%time for n in range(1000000): jctconv.z2h(s)
>>> CPU times: user 19.75 s, sys: 0.06 s, total: 19.81 s Wall time: 19.86 s

unicodedata

考虑到更普通的需求其实就是半角转全角,所以 Python 内置的标准库unicodedata就已经够用了。

%%time
import unicodedata

input = "アイオエオ012345" * 10
for n in range(1000000):
    unicodedata.normalize("NFKC", input)

>>> CPU times: total: 11.5 s Wall time: 11.6 s

虽然是标准库,但转换速度不如上面提到的 mojimoji。

另外,按照 Python 官方文档的说法,还有NFCNFDNFKD三种模式,这里不做过多解释其实是因为我也没搞懂啦 233

下面再提供一个测试用例:

import unicodedata

HAN_MOJI = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヴ゙゚"
ZEN_MOJI = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヴ゙゚"
if ZEN_MOJI == unicodedata.normalize("NFKC", HAN_MOJI):
    print("OK")

手写函数 half_to_fullwidth

前面提到「由于有部分平假名的半角假名是由 2 个字符构成,所以要想全半角的转换的话,得用用字符串替换的方式」,但本人实现后发现这种方式的性能还不如 unicodedata

def half_to_fullwidth(text):
    zenkaku_mapping = {
        "カ": "カ",
        "キ": "キ",
        "ク": "ク",
        "ケ": "ケ",
        "コ": "コ",
        "サ": "サ",
        "シ": "シ",
        "ス": "ス",
        "セ": "セ",
        "ソ": "ソ",
        "タ": "タ",
        "チ": "チ",
        "ツ": "ツ",
        "テ": "テ",
        "ト": "ト",
        "ナ": "ナ",
        "ニ": "ニ",
        "ヌ": "ヌ",
        "ネ": "ネ",
        "ノ": "ノ",
        "ハ": "ハ",
        "ヒ": "ヒ",
        "フ": "フ",
        "ヘ": "ヘ",
        "ホ": "ホ",
        "マ": "マ",
        "ミ": "ミ",
        "ム": "ム",
        "メ": "メ",
        "モ": "モ",
        "ヤ": "ヤ",
        "ユ": "ユ",
        "ヨ": "ヨ",
        "ラ": "ラ",
        "リ": "リ",
        "ル": "ル",
        "レ": "レ",
        "ロ": "ロ",
        "ワ": "ワ",
        "ヲ": "ヲ",
        "ン": "ン",
        "ァ": "ァ",
        "ィ": "ィ",
        "ゥ": "ゥ",
        "ェ": "ェ",
        "ォ": "ォ",
        "ッ": "ッ",
        "ャ": "ャ",
        "ュ": "ュ",
        "ョ": "ョ",
        "ー": "ー",
        "゙": "゛",
        "゚": "゜",
    }

    full_width_text = ""
    for char in text:
        if char in zenkaku_mapping:
            full_width_text += zenkaku_mapping[char]
        else:
            full_width_text += char

    return full_width_text

# 测试函数
text = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヴ゙゚"
converted_text = half_to_fullwidth(text)
print("转换后的全角假名文本:", converted_text)

测试下性能:

%%time
input = "アイオエオ012345" * 10
for n in range(1000000):
    half_to_fullwidth(input)

>>> CPU times: total: 25.2 s Wall time: 25.3 s

单就速度和功能来看,mojimoji 都是第一,但大多数时候只需要半角转全角这一个功能,所以我更推荐内置库 unicodedata 。另外提醒一下: mojimoji 需要 C++环境,打包时会比较麻烦。

其他

打印一定范围内的 Unicode 字符

def print_characters(start_unicode):
    for i in range(start_unicode, start_unicode + 100):
        print(i+" : "chr(i))

# 调用函数并传入起始 Unicode 码作为参数
start_unicode = int(input("请输入起始的 Unicode 码: "))
print_characters(start_unicode)

参考

Pythonで全角・半角を変換(mojimojiなど):非常详细地解释了本文谈到的所有类型转换

Pythonで半角・全角の変換を高速に行う:mojimoji 作者亲自撰写的文档,比较了 3 个常见的半角转全角的第三方库的性能。

Unicode 15.0 Character Code Charts:从官网查看完整的码表。

Loading comments...