卿少納言

卿少納言

JavaScript & Japanese, Python & Polyglot, TypeScript & Translate.
zhihu
github
email
x

淺入深出日語假名轉換

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

平片假名轉換#

結論#

片假名轉平假名

def convert_kata_to_hira(input_text):
    process_texts = []
    for gana in input_text:
        if 12448 <= ord(gana) <= 12534:  # 匹配片假名字符
            hira = chr(ord(gana) - 96)  # 轉換為平假名
            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:  # 匹配平假名字符
            hira = chr(ord(gana) + 96)  # 轉換為片假名
            process_texts.append(hira)
    output_text = "".join(process_texts)
    return output_text

平假名 Unicode#

按 Unicode 組織提供的 Unicode 標準文件 U3040.pdf,平假名的十進制範圍是 12353-12447,十六進制範圍是[\u3041-\u309f](正則表達式的字符轉義使用的是十六進制)。

字符十進制十六進制
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。

字符十進制十六進制
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

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

全半角假名轉換#

半角字符 Unicode#

UFF00.pdf,日語半角字符的範圍是 65381-65439,十六進制範圍:[\uff65-\uff9f]

字符十進制十六進制
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:從官網查看完整的碼表。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。