2010年08月19日

pythonで文章中のアラビア数字を漢数字に変換するスクリプト

日本語読み上げのための mbtts を久しぶりに書き直したのですが、そこでアラビア数字を漢数字に変換する部分も修正しました。
このスクリプトをもっと簡潔に書くとどうなるだろうと、単純な条件にしてみて、考えてみたのが次のスクリプトです。

#!/usr/bin/env python
# -*- coding: utf8 -*-
import re

char2int = {
u'0' :0, u'1' :1, u'2' :2, u'3' :3, u'4' :4,
u'5' :5, u'6' :6, u'7' :7, u'8' :8, u'9' :9,
u'0':0, u'1':1, u'2':2, u'3':3, u'4':4,
u'5':5, u'6':6, u'7':7, u'8':8, u'9':9,
}

numKanji0 = [ u'', u'一', u'二', u'三', u'四', u'五', u'六', u'七', u'八', u'九' ]
numKanji1 = [ u'', u'', u'二', u'三', u'四', u'五', u'六', u'七', u'八', u'九' ]
numKanji = [ numKanji0, numKanji1, numKanji1, numKanji1 ]

numPlace1 = [ u'', u'十', u'百', u'千' ]
numPlace4 = [ u'', u'万', u'億', u'兆', u'京', u'垓' ]

# 千の桁が1のときの処理:
#
# (a) 全て千
#  例  千万,  千五百万,  一万千,  千
#
# (b) 全て一千
#  例 一千万, 一千五百万, 一万一千, 一千
#
# (c) 四桁のときだけ千。残りは一千
#  例 一千万, 一千五百万, 一万一千,  千
#
# (d) 四桁のときは常に千。百十一の桁が無ければ一千、あれば千。
#  例 一千万,  千五百万, 一万一千,  千
#
# (e) 一万以下の千の桁は常に千。一万以上の千の桁は常に一千。
#  例 一千万, 一千五百万,  一万千,  千
#
# (f) 一万以下の千の桁は常に千。一万以上の千の桁は、百十一の桁が無ければ一千、あれば千。
#  例 一千万,  千五百万,  一万千,  千

def convert_pure_integerstring(match):
source = match.group()
numstr = re.sub( u'[,,]', u'', source )
# numKanji[3] = numKanji0 # (b)
# numKanji[3] = numKanji1 if len(numstr) == 4 else numKanji0 # (c),(e)
s = []
for ch in ((u'0'*((4-len(numstr)%4)&3))+numstr): s.insert(0,char2int[ch])

list = []
while len(s):
temp = u''
for i in range(4):
if s[i]: temp = numKanji[i][s[i]] + numPlace1[i] + temp
list.append(temp)
s = s[4:]

if len(list) > len(numPlace4): return source

result = u'' # (a),(b),(c),(d)
# result = list[0][1:] if list[0].startswith(u'一千') else list[0] # (e)
# result = list[0] # (f)
for i in range(len(list)): # (a),(b),(c),(d)
# for i in range(1,len(list)): # (e),(f)
if list[i]:
if len(list) > 1 and list[i] == u'千': list[i] = u'一' + list[i] # (d)
# if list[i] == u'千': list[i] = u'一' + list[i] # (f)
result = list[i] + numPlace4[i] + result

return result if result else u'零'

def convert_integerstring(string):
if string == None or string == u'': return u''
p = re.compile(u'[0-90-9][0-90-9,,]*[0-90-9]|[0-90-9]')
return p.sub( convert_pure_integerstring, string )

print convert_integerstring( u'10,000,000, 15,000,000, 11000, 1000')

このスクリプトは日本語の文章の中のアラビア数字を漢数字に変換します。小数点数には対応していません。
桁区切りのコンマは、無視します。つまり、でたらめでも構いません。
数字は半角でも全角でも混ざっていても処理します。

(追記 2010/08/20)
「一千」の扱いについていろいろ修正してみました。ありがちな読み方を6通り考えてみました。対応する行を有効にしたり、コメントアウトしたりすれば、読み方を変えられます。他にも読み方の規則はあるかもしれませんが、そのときは応用して修正すればできると思います。

(a)と(b)は、はっきりと違和感がありますので、わざわざ選ぶことはないと思います。(c)はここで最初に書いていた処理で、一番シンプルで、違和感は多少ありますが許容範囲内だと思います。(d)と(e)と(f)は(c)の違和感を補正する方向で考えたものです。条件分岐を入れて補正しているので、ちょっと処理が美しくありません。(d)と(e)と(f)のどれを選ぶかは作り手のちょっとした好みによるでしょう。
(追記終わり)

最速とはいえませんが、すっきり分かりやすいアルゴリズムだと思います。
一カ所、for ch in ((u'0'*((4-len(numstr)%4)&3))+numstr): s.insert(0,char2int[ch]) が分かりにくいかもしれませんが、全角か半角の数字だけからなる文字列を先頭に0で詰め物をして4の倍数の桁にした後、変換テーブルを使って逆順の数値リストに変換しています。


拍手する
posted by takayan at 22:51 | Comment(0) | TrackBack(0) | プログラミング | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]


この記事へのトラックバック