pythonで漢数字をアラビア数字に変換しようとしたら、意外に苦戦したので、今後のために記録を残しておきます。
そのようなスクリプトはすでに誰かが書いているだろうと思い、実際検索したらいくつかヒットしたのですが、どれも私にはすぐに理解できず(よほど時間に追われているのでない限り自分が理解できないプログラムは使いたくないです)、自分で数時間かけて作り出しました。
既存のプログラムの中で一番参考にしたのは、Pythonで最もシンプルに文章中の漢数字を数字に変換する方法とプログラム #Python3 – Qiitaです。
ただ、私はそのプログラムをすっきりとは理解できなかったので、引数に漢数字部分だけを渡すという制限を設け、異なる視点から実装しました。
先に結論を示します。
def convert_kansuuji_to_arabic_number(kansuuji):
# 結果として返すアラビア数字の文字列を初期化
arabic_number = ''
# 一〜九の変換表
convert_table = str.maketrans("一二三四五六七八九", "123456789")
# 単位の処理
unit_under_thousand = {"十": 1, "百": 2, "千": 3}
unit_over_thousand = {"万": 4, "億": 8, "兆": 12, "京": 16}
# 基準となる単位の初期化
base_unit = 0
# 一の位から処理していくために逆順にする
reversed_kansuuji = list(reversed(kansuuji))
for i, character in enumerate(reversed_kansuuji):
# 十、百、千の場合
if character in unit_under_thousand.keys():
# unit_under_thousandの値に基準となる単位を加えた桁数でゼロパディング
arabic_number = arabic_number.zfill(unit_under_thousand[character] + base_unit)
# ループの最後(先頭の文字)か、次のループ(一つ前の文字)が単位を表す文字ならば、1を前に付け加える
if i == len(reversed_kansuuji) - 1 or reversed_kansuuji[i+1] in unit_under_thousand or reversed_kansuuji[i+1] in unit_over_thousand:
arabic_number = '1' + arabic_number
# 万、億、兆、京の場合
elif character in unit_over_thousand.keys():
# unit_over_thousandの値の桁数でゼロパディング
arabic_number = arabic_number.zfill(unit_over_thousand[character])
# 基準となる単位を更新
base_unit = unit_over_thousand[character]
# 一〜九の場合
else:
# 一〜九の変換表を用いて変換した数字を前に付け加える
arabic_number = character.translate(convert_table) + arabic_number
return arabic_number
# 実験
test1 = convert_kansuuji_to_arabic_number('五千京二百三十億八百六十五')
test2 = convert_kansuuji_to_arabic_number('千二十三')
test3 = convert_kansuuji_to_arabic_number('百十')
print(test1)
print(test2)
print(test3)
かなり細かくコメントを付けたので、上記のコードとコメントを読むだけで理解できるかもしれませんが、考え方を述べます。
一気に処理しようとするとややこしく感じられたので、一の位から(漢数字表記をしたときに右の文字から)一文字ずつループで処理するという見通しを立てました。
一〜九は1〜9の数字に変換するだけです。
「二万五千四百二十三」などのように、すべての桁に一〜九の漢数字が入っているなら、十、百、千、万といった単位を表す漢数字を消すだけで完成です。
しかし、「千二十三」のように飛んでいる位があると、それではうまくいきません。
この例の「千」なら23ではなく023になるように、適切な桁数でゼロパディングすればいいですね。
問題は、「五千京二百三十億八百六十五」のような大きい数字の場合、「八百六十五」の「十」と「二百三十億」の「十」の桁数が違うことです。
言い換えると、十、百、千は何度も登場することがありますが、万、億、兆、京は一度しか登場しません。
このように、単位を表す漢数字を、千以下のものと千より大きいものとに区別できます。
「八百六十五」の「十」を1桁だとすると、「二百三十億」の「十」は9桁です。
万、億、兆、京を処理するたびに、基準となる単位を4, 8, 12, 16と設定すればよいと気づきました。
あとは「千二十三」が023、「百十」が00となってしまうように、単位を表す漢数字の処理で1が抜けてしまうという問題に対処すれば完成です。
この問題は、一番上の位が単位を表す漢数字である場合か、「百十」のように単位を表す漢数字が連続する場合に発生します。
そこで、コード内のコメントにもありますように、ループの最後(先頭の文字)か、次のループ(一つ前の文字)が単位を表す文字ならば、1を前に付け加えるという処理を書きました。
これが私にとって理解しやすい考え方です。
壱弐参なども変換したければ変換表に加えればいいですし、京より上の単位にも対応したければ「”垓”: 20」のように単位を追加すればいいです。
よろしければ上記のコードと考え方をご活用ください。