浅入深出日语假名转换

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

平片假名转换

结论

片假名转平假名

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](正则表达式的字符转义使用的是十六进制)。

Character Decimal Hexadecimal
12353 3041
12354 3042
12355 3043
12356 3044
12357 3045
12358 3046
12359 3047
12360 3048
12361 3049
12362 304A
12363 304B
12364 304C
12365 304D
12366 304E
12367 304F
12368 3050
12369 3051
12370 3052
12371 3053
12372 3054
12373 3055
12374 3056
12375 3057
12376 3058
12377 3059
12378 305A
12379 305B
12380 305C
12381 305D
12382 305E
12383 305F
12384 3060
12385 3061
12386 3062
12387 3063
12388 3064
12389 3065
12390 3066
12391 3067
12392 3068
12393 3069
12394 306A
12395 306B
12396 306C
12397 306D
12398 306E
12399 306F
12400 3070
12401 3071
12402 3072
12403 3073
12404 3074
12405 3075
12406 3076
12407 3077
12408 3078
12409 3079
12410 307A
12411 307B
12412 307C
12413 307D
12414 307E
12415 307F
12416 3080
12417 3081
12418 3082
12419 3083
12420 3084
12421 3085
12422 3086
12423 3087
12424 3088
12425 3089
12426 308A
12427 308B
12428 308C
12429 308D
12430 308E
12431 308F
12432 3090
12433 3091
12434 3092
12435 3093
12436 3094
12437 3095
12438 3096
12439 3097
12440 3098
12441 3099
12442 309A
12443 309B
12444 309C
12445 309D
12446 309E
12447 309F

片假名 Unicode

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

Character Decimal Hexadecimal
12448 30A0
12449 30A1
12450 30A2
12451 30A3
12452 30A4
12453 30A5
12454 30A6
12455 30A7
12456 30A8
12457 30A9
12458 30AA
12459 30AB
12460 30AC
12461 30AD
12462 30AE
12463 30AF
12464 30B0
12465 30B1
12466 30B2
12467 30B3
12468 30B4
12469 30B5
12470 30B6
12471 30B7
12472 30B8
12473 30B9
12474 30BA
12475 30BB
12476 30BC
12477 30BD
12478 30BE
12479 30BF
12480 30C0
12481 30C1
12482 30C2
12483 30C3
12484 30C4
12485 30C5
12486 30C6
12487 30C7
12488 30C8
12489 30C9
12490 30CA
12491 30CB
12492 30CC
12493 30CD
12494 30CE
12495 30CF
12496 30D0
12497 30D1
12498 30D2
12499 30D3
12500 30D4
12501 30D5
12502 30D6
12503 30D7
12504 30D8
12505 30D9
12506 30DA
12507 30DB
12508 30DC
12509 30DD
12510 30DE
12511 30DF
12512 30E0
12513 30E1
12514 30E2
12515 30E3
12516 30E4
12517 30E5
12518 30E6
12519 30E7
12520 30E8
12521 30E9
12522 30EA
12523 30EB
12524 30EC
12525 30ED
12526 30EE
12527 30EF
12528 30F0
12529 30F1
12530 30F2
12531 30F3
12532 30F4
12533 30F5
12534 30F6
12535 30F7
12536 30F8
12537 30F9
12538 30FA
12539 30FB
12540 30FC
12541 30FD
12542 30FE
12543 30FF

片假名语音扩展 Unicode

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

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

Character Decimal Hexadecimal
12784 31F0
12785 31F1
12786 31F2
12787 31F3
12788 31F4
12789 31F5
12790 31F6
12791 31F7
12792 31F8
12793 31F9
12794 31FA
12795 31FB
12796 31FC
12797 31FD
12798 31FE
12799 31FF

全半角假名转换

半角字符 Unicode

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

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

注:第一个字符是半角的

观察上面的表格,可以注意到下面 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:从官网查看完整的码表。