摘要:知其然更要知其所以然。
<!-- more -->
# 平片假名转换
## 结论
片假名转平假名
```python
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
```
平假名转片假名
```python
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](https://www.unicode.org/charts/PDF/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](https://www.unicode.org/charts/PDF/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](https://www.unicode.org/charts/PDF/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](https://www.unicode.org/charts/PDF/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. 平假名没有半角假名(这一点可以和维基百科的[半形假名](https://zh.wikipedia.org/zh-cn/%E5%8D%8A%E5%BD%A2%E5%81%87%E5%90%8D)相印证)
2. 有部分平假名的半角假名是由 2 个字符构成,所以不可能像平片假名转换那样用`hira = chr(int(ord(gana) - 96))`轻松转换,而是要用字符串替换的方式。
所以,全半角假名的转换比平片假名的转换麻烦得多,个人建议使用第三方库。
## mojimoji
如果想从全角转为半角,那么 mojimoji 可能是唯一的选择了。
```python
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で半角・全角の変換を高速に行う](https://qiita.com/ikuyamada/items/fea6c8f81e7cac7cf318)提供的测评来看,mojimoji 也是速度最快的。
```python
%%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`就已经够用了。
```python
%%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 官方文档](https://docs.python.org/3/library/unicodedata.html)的说法,还有`NFC`、`NFD` 、`NFKD`三种模式,这里不做过多解释~~其实是因为我也没搞懂啦 233~~
下面再提供一个测试用例:
```python
import unicodedata
HAN_MOJI = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヴ゙゚"
ZEN_MOJI = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヴ゙゚"
if ZEN_MOJI == unicodedata.normalize("NFKC", HAN_MOJI):
print("OK")
```
## 手写函数 half_to_fullwidth
前面提到「由于有部分平假名的半角假名是由 2 个字符构成,所以要想全半角的转换的话,得用用字符串替换的方式」,但本人实现后发现这种方式的性能还不如 unicodedata
```python
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)
```
测试下性能:
```python
%%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 字符
```python
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など)](https://note.nkmk.me/python-str-convert-full-half-width/):非常详细地解释了本文谈到的所有类型转换
[Pythonで半角・全角の変換を高速に行う](https://qiita.com/ikuyamada/items/fea6c8f81e7cac7cf318):mojimoji 作者亲自撰写的文档,比较了 3 个常见的半角转全角的第三方库的性能。
[Unicode 15.0 Character Code Charts](https://www.unicode.org/charts/):从官网查看完整的码表。