1
1

More than 3 years have passed since last update.

Pythonで数字to漢数字の変換

Posted at

きっかけ

テストケースで1郎2郎3郎...みたいなのを作ってたんですが、
名前にアラビア数字というのもいかがなものかと思いまして、変換してやるようにしました。

int2kanint

python3.7.5で動いてます

int2kanint
# -*- coding: utf-8 -*-

import csv

def int2kanint(num) :
    suji   = ["","一","二","三","四","五","六","七","八","九"]
    kugiri = ["","十","百","千"]
    tani   = ["","万","億","兆","京","垓","𥝱","穣","溝","澗","正","載","極","恒河沙","阿僧祇","那由他","不可思議","無量大数"]

    num  = list(map(int,list(str(num))))
    kansuji = []

    for k, v in zip(range(len(num)), reversed(num)) :
        keta = []
        keta.append(suji[v if v>2 else 0 if k%4 else v])
        keta.append(kugiri[k%4 if v>0 else 0])
        keta.append((tani[0 if k%4 else int(k/4) if any(list(reversed(num))[k:(k+4 if len(num)>=(k+4) else len(num))]) else 0]))
        kansuji.append("".join(keta))

    kansuji = "".join(reversed(kansuji))

    return kansuji if kansuji else "零"

以下に説明を連ねていきます。

変数

説明が怪しい日本語になってますが、何となく察していただけると幸いです。

変数名 意味
suji 一から九までの漢数字です string[]
kugiri 十,百,千といった区切りです string[]
tani 四桁区切りの単位です string[]
num 変換前のアラビア数字です int → int[]
keta 四桁区切り毎の漢数字です string[]
kansuji 変換後の漢数字です string[] → string

変換方法

suji,kugiri,taniでそれぞれ分けて、最後に結合する形をとっています。
私が習った漢数字のルールを基に、それぞれ変換を行っていきます。
地域差、業界差あるとは思いますがそこは一旦置いておきましょう。

suji

それぞれの桁の値を表す一から九の漢数字には以下のルールがあります。

  • 値が0でない限り、は省略する
    • 例:4203 → 四千二百三
    • とする場合がありますが今回は含めません
  • ,,の前にくるは省略する
    • 例:11111 → 一万千百十一

よって
値が2以下かつ一の位ではないのであれば省略、それ以外は出力
と考えれば良いでしょう。
そのため、sujiを出す為の処理は以下のようになります。

suji
suji[0 if v<2 and k%4 else v]

sujiはインデックスと漢数字が連動してるリストです。
上記の条件を満たしていれば空文字を、それ以外の場合は漢数字を返す形になります。
(関数でもないのに返すというのも変ですが)

kugiri

漢数字では四桁毎に数を区切りますが、その中で十,百,千とさらに桁を区切ります。
その際には以下のようなルールがあります。

  • 桁に該当する値がなければ、省略する
    • 例:2035070 → 二百三万五千七十

これだけですね。

kugiri
kugiri[k%4 if v>0 else 0]

内容もシンプルです。

tani

漢数字では四桁毎に数を区切りる際には以下のルールがあります。

  • 四桁の中身がなければ、単位を表す漢数字は省略する
    • 例:123400005678 → 千二百三十四億五千六百七十八

ここだけ見ればシンプルなんですが、私のアルゴリズムではかなりややこしくなってしまいました。
というのも、与えられた値を漢数字に変換する際に、以下のようなfor文で各桁を参照しています。

for文
for k, v in zip(range(len(num)), reversed(num)) :

kにはインデックス、vには各桁の値が入るわけですが、reversed(num)としてるように一桁目から見てるわけです。
処理としては 四桁毎に単位を出力する と考えるのですが、四桁の中身がない場合、単位を省略しなければなりません。
そうすると必然的に四桁先を見ればいいだけなんですが、値が四桁もない場合を考慮しなければならないわけです。

解決策としては以下のとおりです。

tani
tani[0 if k%4 or not any(list(reversed(num))[k:(k+4 if len(num)>=(k+4) else len(num))]) else int(k/4)]

値が十,百,千の桁であるまたは四桁の中身が1つもないのであれば省略、それ以外は出力という形に落ち着きました。
anyで四桁分の値を参照しに行くのですが、四桁先がリストの末尾を超えた場合、末尾まで参照する、といった感じです。

終わりに

競技プログラミングとも言えなくもない、アルゴリズムを考える課題は大好きなので、ついつい熱中して作ってしまいました。
数字to漢数字はいくらでもやりようがあると思うのですが、リストを使ったやり方は今回初めて思いついたので、とても楽しかったです。
高速化・簡易化できる書き方がありましたら、ぜひともご教授ください!

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1