HOME Python download 書き込む

47 行の かな変換ライブラリ


1. 初めに

仕事で全角カタカナと半角カタカナを相互に変換する必要が生じたのと、 Python 3 用のものは見当たらなかったので 自分で書いてみました。

以下 (kana.py) に示すように、意外と簡潔に書くことができます。 説明と、テストコードを除いた本体は 47 行で済みます。

2. 仕様と使い方

全角カタカナ、半角カタカナ、ひらがなを相互に変換することができます。 平仮名には 「ヴ」に対応する文字がないので、かわりに 「う゛」で表します。 数字については、半角カタカナでは半角で、全角カタカナ及び平仮名では全角で表します。 変換関数は以下の6種類です。
jz(s)
文字列 s に含まれる平仮名を全角カタカナに置き換えます。
jh(s)
文字列 s に含まれる平仮名を半角カタカナに置き換えます。
zj(s)
文字列 s に含まれる全角カタカナを平仮名に置き換えます。
zh(s)
文字列 s に含まれる全角カタカナを半角カタカナに置き換えます。
hj(s)
文字列 s に含まれる半角カタカナを平仮名に置き換えます。
hz(s)
文字列 s に含まれる半角カタカナを全角カタカナに置き換えます。
次の様に、import して使います、kana.py は sys.path で定義されるパスのどれか (例えば、[Python をインストールしたディレクトリ]/lib/site-packages)に入れておくか、 呼び出すスクリプトと同じディレクトリに入れておきます。
#!python
#coding:utf8

import kana

def pprint(*ls):
    print(*ls, sep='\n', end='\n\n')

if __name__=='__main__':
    for s in ['てんはひとのうえにひとをつくらずひとのしたにひとをつくらず', \
              'ほんじつはせいてんなれどもなみたかし', \
              'わがはいはねこである', \
             ]:
        pprint(s, kana.jz(s), kana.jh(s))

    for s in ['テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ', \
              'ホンジツハセイテンナレドモナミタカシ', \
              'ワガハイハネコデアル', \
             ]:
        pprint(s,  kana.zh(s), kana.zj(s))

    for s in ['テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ', \
              'ホンジツハセイテンナレドモナミタカシ', \
              'ワガハイハネコデアル', \
              ]:
        pprint(s, kana.hz(s), kana.hj(s))
実行結果
Z:\doc\monthly\12-11\kana>d:\python32\python.exe test_kana.py         
てんはひとのうえにひとをつくらずひとのしたにひとをつくらず
テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ
テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ

ほんじつはせいてんなれどもなみたかし
ホンジツハセイテンナレドモナミタカシ
ホンジツハセイテンナレドモナミタカシ

わがはいはねこである
ワガハイハネコデアル
ワガハイハネコデアル

テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ
テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ
てんはひとのうえにひとをつくらずひとのしたにひとをつくらず

ホンジツハセイテンナレドモナミタカシ
ホンジツハセイテンナレドモナミタカシ
ほんじつはせいてんなれどもなみたかし

ワガハイハネコデアル
ワガハイハネコデアル
わがはいはねこである

テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ
テンハヒトノウエニヒトヲツクラズヒトノシタニヒトヲツクラズ
てんはひとのうえにひとをつくらずひとのしたにひとをつくらず

ホンジツハセイテンナレドモナミタカシ
ホンジツハセイテンナレドモナミタカシ
ほんじつはせいてんなれどもなみたかし

ワガハイハネコデアル
ワガハイハネコデアル
わがはいはねこである

3. 動作環境

以下の環境で動作を確認しています。

4. 制約

&#x80 --&#xA0 の文字を含む文字列は正しく変換されません。 ただ、これらの文字は制御コードなので、通常の文字列に含まれることはまずありません。

5. 概要

基本的には、Python の str.translate(trans_table) をつかって変換します。 trans_table は文字の変換方法を規定した辞書です。これは後述する str.maketrans() を使って作成します。 変換テーブルのキーは文字である必要があります。そのため、半角カタカナの濁音は translate メソッドをもちいて直接変換できません。 (半角カタカナの濁音は「カ」などの文字と濁点、半濁点の2文字からなっているため。) そこで、半角カタカナの濁音を 通常の文字列には含まれない制御文字 (&#x80 --&#xA0) に置き換えて、それを変換テーブルで変換します。
さらに、平仮名には「ヴ」に相当する文字がないので、「う」と「゛」で表します。平仮名から変換するときは、 「う゛」を「ヴ」に置き換えてから変換テーブルで変換します。また、平仮名に変換するときは変換テーブルで変換した結果の 「ヴ」を「う゛」に置き換えます。

変換テーブルを作成するのに、str.maketrans(x [,y [,z]]) を使います。 str.maketrans() の引数には文字をキーとする辞書、あるいは、等しい長さの2つの文字列を渡すことができます。 辞書を渡す場合、キーは文字である必要がありますが、値は文字列でよいので、1文字を文字列に変換することができます。 そのため全角の濁音 (例えば「ガ」や「が」) を半角の濁音 (「ガ」)に変換することができます。 文字列を引数に渡す場合、1番目の文字列を2番目の文字列に変換する変換テーブルを返します。また、 3番目の引数は変換後の文字列には含めない文字の文字列を渡します。 str.maketrans() についての詳しい説明は、 python のドキュメント を見てください。

6. ソースコードと簡単な説明

[code 1] にソースを示します。 かな変換を行っているのは 30 -- 77 行で、47 行で書くことができます (1,2 行目はなくても良いおまじない、3--28 行目は説明、80 行目以降は簡単なテストコード)。
001:   #!python3
002:   #coding:utf8
003:   
004:   """
005:   かな変換ライブラリ
006:   
007:   変換関数:
008:   zj(s): s に含まれる 全角カタカナをひらがなに変換
009:   jz(s): s に含まれる ひらがなを全角カタカナに変換
010:   zh(s): s に含まれる 全角カタカナを半角カタカナに変換
011:   jh(s): s に含まれる ひらがなを半角カタカナに変換
012:   hz(s): s に含まれる 半角カタカナを全角カタカナに変換
013:   hj(s): s に含まれる 半角カタカナをひらがなに変換
014:   
015:   ノート
016:   str.translate メソッドをつかって変換。
017:   それに加えて以下の操作を行う
018:   
019:   半角カタカナから変換する場合:
020:   半角カタカナの濁音は2文字で表されるので、それらを正規表現を用いて制御文字1文字 (&#x80 --&#xA0)
021:   におきかえてから translate を用いて全角の濁音に置き換える。
022:   
023:   平仮名から変換する場合:
024:   う゛を ヴ に置き換えてから translate で変換する。
025:   
026:   平仮名に変換する場合
027:   translate の結果の ヴ を う゛に置き換える。
028:   """
029:   
030:   import re, functools
031:   
032:   C_OFF = 0x80
033:   
034:   S_HIR = '、。「」 ー0123456789' \
035:             'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん' \
036:               'っぁぃぅぇぉゃゅょ' \
037:                 'がぎぐげござじずぜぞだぢづでどばびぶべぼヴ' \
038:                   'ぱぴぷぺぽ' \
039:   
040:   S_ZEN = '、。「」 ー0123456789' \
041:             'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン' \
042:               'ッァィゥェォャュョ'  \
043:                 'ガギグゲゴザジズゼゾダヂヅデドバビブベボヴ' \
044:                   'パピプペポ'
045:   
046:   S_SEI = '、。「」 -0123456789'  \
047:              'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン' \
048:                'ッァィゥェォャュョ'
049:   S_DAK ='カキクケコサシスセソタチツテトハヒフヘホウ'
050:   S_HAT ='ハヒフヘホ'
051:   TENN, MARU = '゙', '゚'
052:   
053:   L_DAK = [c+TENN for c in S_DAK] + [c+MARU for c in S_HAT]
054:   S_CCH = ''.join(chr(i+C_OFF) for i in range(len(L_DAK)))
055:   S_HAN = S_SEI + S_CCH
056:   L_HAN = list(S_SEI) + L_DAK
057:   H_DAK = dict(zip(L_DAK, S_CCH))
058:   R_DAK = re.compile('[{}]{}|[{}]{}'.format(S_DAK, TENN, S_HAT, MARU))
059:   
060:   def comb(*fs):
061:       return lambda x: functools.reduce(lambda y,f:f(y), fs, x)
062:   
063:   def trans(k,v):
064:       assert len(k)==len(v)
065:       tbl = str.maketrans(k,v) if type(v) is str else \
066:             str.maketrans(dict(zip(k,v)))
067:       return lambda s: s.translate(tbl)
068:   
069:   def replace(c0, c1): return lambda s: s.replace(c0,c1)
070:   subd = lambda s: R_DAK.sub(lambda m:H_DAK[m.group()], s)
071:   
072:   zj=comb(trans(S_ZEN, S_HIR), replace('ヴ', 'う゛'))
073:   jz=comb(replace('う゛', 'ヴ'), trans(S_HIR, S_ZEN))
074:   zh=trans(S_ZEN, L_HAN)
075:   jh=comb(replace('う゛', 'ヴ'), trans(S_HIR, L_HAN))
076:   hz=comb(subd, trans(S_HAN, S_ZEN))
077:   hj=comb(subd, trans(S_HAN, S_HIR), replace('ヴ', 'う゛'))
078:   
079:   
080:   def test(name, fun, s):
081:       print(name, 'ok' if s==fun(s) else 'ng', sep=' .... ')
082:       
083:   if __name__=='__main__':
084:       test('zen>han>zen', comb(zh, hz), S_ZEN)
085:       test('zen>hira>zen', comb(zj, jz), S_ZEN)
086:       
087:       s_hir = S_HIR.replace('ヴ', 'う゛')
088:       test('hira>zen>hira', comb(jz, zj), s_hir)
089:       test('hira>han>hira', comb(jh, hj), s_hir)
090:       
091:       s_han=''.join(L_HAN)
092:       test('han>hira>han', comb(hj, jh), s_han)
093:       test('han>zen>han', comb(hz, zh), s_han)

6.1. 定数

C_OFF
半角カタカナの濁音を1文字に変換する際の文字コードのオフセットです。
つまり、半角カタカナの濁音は C_OFF + i の文字に変換されます。(S_CCH, R_DAK の説明を参照。)
S_HIR
平仮名に含まれる文字からなる文字列です。
「う゛」は2文字になるので、ここでは「ヴ」で表しておきます。(前述したように変間前後で置き換えます。)
S_ZEN
全角カタカナに含まれる文字からなる文字列です。
S_SEI
半角カタカナの清音からなる文字列です。
S_DAK
「゛」をつけると濁音になる文字からなる文字列です。
S_HAT
「゜」をつけると半濁音になる文字からなる文字列です。
TENN, MARU
半角の濁点と半濁点です。
L_DAK
半角カタカナの濁音を表す文字列のリストです。
S_CCH
&#x80 から始まる文字列です。
S_HAN
変換テーブルのキーとして用いる半角カタカナの文字です。 清音を表す文字と、&#x80 からの制御文字からなります。
L_HAN
変換テーブルの値になる半角カタカナの文字列のリストです。
H_DAK
半角カタカナの濁音を表す文字列と制御文字とを対応付けるハッシュ表です。
R_DAK
半角カタカナの濁音の正規表現です。

6.2. 関数

comb(*fs)
fs で与えられた関数を合成した関数を返す関数です。
例えば、 comb(fa, fb, fc) は lambda x: fc(fb(fa(x))) を返します。
trans(k,v)
変換テーブルを用いて、k の文字を v の文字あるいは文字列に置き換える関数を返す関数です。
replace(c0, c1)
文字列の中の c0 を c1 に変換した文字列を返す関数を返す関数です。
subd
文字列のなかの半角カタカナの濁音を制御文字に置き換える関数です。
zj
全角カタカナを平仮名に置き換える関数です。変換テーブルで S_ZEN を S_HIR に置き換えたあと、「ヴ」を「う゛」に置き換えます。
jz
平仮名を全角カタカナに変換する関数です。「う゛」を「ヴ」に置き換えたあと、変換テーブルで S_HIR を S_ZEN に変換します。
zh
全角カタカナを半角カタカナにおきかえる関数です。変換テーブルで S_ZEN を L_HAN に置き換えます。
jh
平仮名を半角カタカナに置き換える関数です。「う゛」を「ヴ」に置き換えたあと、変換テーブルで S_HIR を L_HAN に置き換えます。
hz
半角カタカナを全角カタカナに置き換える関数です。濁音を表す文字列を制御文字に置き換えたあと、変換テーブルを用いて S_HAN を L_ZEN に置き換えます。
hj
半角カタカナを平仮名に置き換えます。濁音を表す文字列を制御文字に置き換え、変換テーブルで S_HAN を S_HIR に置き換え、さらに、「ヴ」を「う゛」に置き換えます。

7. 終わりに

カナ変換プログラムは冗長なコードになると思われがちですが、 意外と簡潔に書くことができます。
このコードには関数型のテクニックをふんだんに使っています。 関数型を使うとこのようなライブラリを簡潔に書くことができます。