こうかの雑記

こうかの雑記

昔の懐かしいこと、ubuntuのこと、その他いろいろ

mozc用の郵便番号辞書を作るpythonスクリプト

はじめに

 以前にubuntuで使われている日本語入力システムmozcのために郵便番号辞書データを作って公開しました。その時点では適当なタイミングで新しい郵便番号辞書データを作った都度、公開するつもりでした。郵便番号辞書データを作ることは簡単にできるですが、アップロードして記事を書くまでの手間が意外と手間に感じました。そこで、拙ない出来で恥ずかしいのですがスクリプトそのものを公開することにしました。

 linuxを使っている人でpython3を使える環境があれば使えると思います。自分が必要とするタイミングで最新版の辞書を作ることが出来るようになります。

 尚、ubuntupythonにはtkinterは含まれていませんので、次のコマンドを実行してインストールして下さい

sudo apt-get -y install python3-tk

郵便番号データの準備

 辞書データの元になる郵便番号データを日本郵政株式会社の次のサイトからダウンロードします。

郵便番号データダウンロード

 住所の郵便番号(CSV形式)の中の「読み仮名データの促音・拗音を小書きで表記しないもの」と「読み仮名データの促音・拗音を小書きで表記するもの」のいずれか好きな方を選びます。わたしは「読み仮名データの促音・拗音を小書きで表記するもの」を使っています。

  都道府県一覧の一番下に全国一括がありますので、これをダウンロードします。

(参考)郵便番号データについて

 全国一括のファイル名はKEN_ALL.ZIPとなっています。解凍するとKEN_ALL.CSVというテキストファイルになります。
 文字のエンコードはShift-JISが使われていますので、確認するためにCalcで読み込む場合ははShift-JISまたはWindows-932を指定すると文字化けなく読み込めます。エディタgeditの場合は自動判別して読み込んでくれます。

 1行1件で、1行は15の項目で構成されています。詳しくは「日本郵政株式会社の郵便番号データについて」を参照下さい。

スクリプトの実行

 スクリプトをこの記事の末尾に掲載しておきますが、スクリプトを取り出してmakpostaldic.pyの名前でファイルに保存した後、実行権限を付与しておいて下さい。そうすればこのファイルのアイコンをダブルクリックするだけで起動できるようになります。

 マウスのダブルクリックで起動した場合はファイル選択画面が表示されますので、KEN_ALL.CSVを選択します。

 端末画面のコマンドラインから起動することもできますが、その場合は郵便番号データのあるディレクトリに移動しておくと良いでしょう。というのはコマンドラインからの実行は入力ファイル名をパラメータとして与える必要が有ります。

$ ./makpostaldic.py KEN_ALL.CSV

 結果は現在いるディレクトリに出力されます。
 出力ファイルの名前はzipdicになります。

mozcの辞書に登録

  1. mozcツールの中に有る辞書ツールを開きます。

  2. 既に「郵便番号辞書」がある場合は先にこれを削除します。
     既存の「郵便番号辞書」 を選んで→「管理」→「選択した辞書を削除」とします。

  3. 辞書の追加は、「管理」→「新規辞書にインポート」をクリックします。
     ファイル:先程用意した"zipdic"を(ファイルを選択)ボタンで選んで指定
     辞書名 :「郵便番号辞書」
     フォーマット:自動判定
     エンコード :自動判定

  4. インポートをクリックします。

 以上で「郵便番号辞書」が作られます。

 尚、mozcのプロパティで"郵便番号変換"の項目には初期値でチェックが付いているとは思いますが、念のため確認しておいて下さいね。これにチェックが付いてないと郵便番号変換できません。

スクリプト


#!/usr/bin/env python3
"""
Mozc用郵便番号辞書データを作る。
makpostaldic.py

2016/10/15 初版 郵便番号データ中の"("以後は無視している
2018/05/31 半角→全角の変換機能をpythonのコードに置き換え
2019/08/29 町の記述中の'('以後について対応
2019/08/29 半角→全角の変換機能をクラス化
2019/09/04 町の記述中、'('よりも前に'〜'が来る場合の簡易的対応した。
2020/02/19 コマンドライン用に入力ファイル名を引数で受け取れるようにした。
2020/03/15 余分なself.の記述を削除
@author: sk(koukaforest)
"""
# 注 ~は、'から'を日本語変換して得た文字とは違う。元データから取得した文字
import tkinter.filedialog as tkfd
import codecs
import csv
import sys, os

# 半角から全角への片方向変換のためのクラス
class Hanmoji:
    def __init__(self):
        #2018/05/31 追加 半角-全角テーブル
        self.dic1 = str.maketrans({
            'ア':'ア', 'イ':'イ', 'ウ':'ウ','エ':'エ', 'オ':'オ', 
            'カ':'カ', 'キ':'キ', 'ク':'ク', 'ケ':'ケ', 'コ':'コ',
            'サ':'サ', 'シ':'シ', 'ス':'ス', 'セ':'セ', 'ソ':'ソ',
            'タ':'タ', 'チ':'チ', 'ツ':'ツ', 'テ':'テ', 'ト':'ト',
            'ナ':'ナ', 'ニ':'ニ', 'ヌ':'ヌ', 'ネ':'ネ', 'ノ':'ノ',
            'ハ':'ハ', 'ヒ':'ヒ', 'フ':'フ', 'ヘ':'ヘ', 'ホ':'ホ',
            'マ':'マ', 'ミ':'ミ', 'ム':'ム', 'メ':'メ', 'モ':'モ',
            'ヤ':'ヤ', 'ユ':'ユ', 'ヨ':'ヨ', 
            'ラ':'ラ', 'リ':'リ', 'ル':'ル', 'レ':'レ', 'ロ':'ロ',
            'ワ':'ワ', 'ン':'ン', 
            'ァ':'ァ', 'ィ':'ィ', 'ゥ':'ゥ', 'ェ':'ェ', 'ォ':'ォ', 
            'ッ':'ッ', 'ャ':'ャ', 'ュ':'ュ', 'ョ':'ョ',
            '1':'1','2':'2', '3':'3', '4':'4', '5':'5',
            '6':'6','7':'7', '8':'8', '9':'9', '0':'0',
            'A':'A','B':'B', 'C':'C', 'D':'D', 'E':'E',
            'F':'F','G':'G', 'H':'H', 'I':'H', 'J':'J',
            'K':'K','L':'L', 'M':'M', 'N':'N', 'O':'O',
            'P':'P','Q':'Q', 'R':'R', 'S':'S', 'T':'T',
            'U':'U','V':'V', 'W':'W', 'X':'X', 'Y':'Y',
            'Z':'Z',
            'a':'a', 'b':'b', 'c':'c', 'd':'d', 'e':'e', 
            'f':'f', 'g':'g', 'h':'h', 'i':'i', 'j':'j', 
            'k':'k', 'l':'l', 'm':'m', 'n':'n', 'o':'o', 
            'p':'p', 'q':'q', 'r':'r', 's':'s', 't':'t', 
            'u':'u', 'v':'v', 'w':'w', 'x':'x', 'y':'y', 
            'z':'z','-':'−','ー':'ー',',':',','.':'.',
            '、':'、', '#':'#' })
        self.dic2 = dict({
            'ウ':'ヴ',
            'カ':'ガ', 'キ':'ギ', 'ク':'グ', 'ケ':'ゲ', 'コ':'ゴ',
            'サ':'ザ', 'シ':'ジ', 'ス':'ズ', 'セ':'ゼ', 'ソ':'ゾ',
            'タ':'ダ', 'チ':'ヂ', 'ツ':'ヅ', 'テ':'デ', 'ト':'ド',
            'ハ':'バ', 'ヒ':'ビ', 'フ':'ブ', 'ヘ':'ベ', 'ホ':'ボ'})
        self.dic3 =dict({
            'ハ':'パ', 'ヒ':'ピ', 'フ':'プ', 'ヘ':'ペ', 'ホ':'ポ'})

    # 半角文字列を受取、全角文字を返す
    def han2zen(self, strings):
        mojiretu= strings.translate(self.dic1) #単独文字の置き換え
        p = mojiretu.find('゙',0)
        while p != -1:                     #濁音文字の置き換え
            k = mojiretu[p-1]
            v =self.dic2[k]
            mojiretu = mojiretu[0:p-1]+v+mojiretu[p+1: ]
            p =mojiretu.find('゙',p)
        p = mojiretu.find('゚',0)
        while p != -1:                     #半濁音文字の置き換え
            k = mojiretu[p-1]
            v = self.dic3[k]
            mojiretu = mojiretu[0:p-1]+v+mojiretu[p+1: ]
            p = mojiretu.find('゚',0)
        return mojiretu

def output_rtn(outf, zip, kana, todoufuken,city, town_h, w_town):
    # 1件分の頭部分として郵便番号、都道府県と市の部分を作成
    wrow = zip[0: 3] + '-' + zip[3: 7] + '\t'+ todoufuken + city
    # 町以下の部分(列挙されている)分を頭部分に加えて1件分の行データに
    lst= w_town.split('、')
    for twn in lst:
        row = wrow + town_h + twn + '\t短縮よみ\t' + kana + '\n'
        outf.write(row)

def main_rtn(argv):
    if len(argv) == 2:
        cdir = os.getcwd()
        filename=cdir + '/' + argv[1]
    else:
        filename = tkfd.askopenfilename(title='makpostaldic.py')
    dirname = os.path.dirname(filename)
    os.chdir(dirname)

    moji = Hanmoji()
    inf=codecs.open(filename,'r','sjis')
    #inf=codecs.open(filename,'r','UTF-8')
    outf=codecs.open('zipdic','w','UTF-8')
    reader = csv.reader(inf,delimiter=',')
    w_town= ''
    w_kana_town = ''
    rekkyo_sw = ''
    rekkyo_sw_k = ''
    for rec in reader:
        zip             = rec[2]
        kana_todoufuken = rec[3]
        kana_city       = rec[4]
        kana_town       = rec[5]
        todoufuken      = rec[6]
        city            = rec[7]
        town            = rec[8]
        if town=='以下に掲載がない場合':
            town = ''
            kana_town = ''
        s = town.find('の次に番地がくる場合', 0)
        if s != -1:
            town = ''
            s = kana_town.find('ノツギニバンチガクルバアイ', 0)
            kana_town = ''
        s = town.find('(無番地を除く)', 0)
        if s != -1:
            town = town[0: s]
            s = kana_town.find('(ムバンチヲノゾク)', 0)
            kana_town = kana_town[0: s]
        s = town.find('無番地', 0)
        if s != -1:
            town = town[0: s] + town[s+3: ]
            s = kana_town.find('ムバンチ',0)
            kana_town = kana_town[0: s] + kana_town[s+5: ]
        s = town.find('(次のビルを除く)', 0)
        if s != -1:
            town = town[0: s]
            s = kana_town.find('(ツギノビルヲノゾク)', 0)
            kana_town = kana_town[0: s]
        s = town.find('(地階・階層不明)', 0)
        if s != -1:
            town = town[0: s]
            s = kana_town.find('(チカイ・カイソウフメイ)', 0)
            kana_town = kana_town[0: s]
        s = town.find('(その他)', 0)
        if s != -1:
            town = town[0: s]
            s = kana_town.find('(ソノタ)', 0)
            kana_town = kana_town[0: s]

        s = town.find('(',0)
        kana_s = kana_town.find('(',0)
        e = town.find(')',0)
        kana_e = kana_town.find(')',0)
        k  = town.find('〜', 0)
        if rekkyo_sw == '':   #town内の"("の前にある部分
                              #列挙の存在スイッチを兼ねている
            if s != -1:       #'('がある場合
                if k != -1:
                    if k < s :                  #'~'が'('の前にある時
                        town = town[0: s]     #'('から後ろを展開しない様にする

                rekkyo_sw = town[0:s]
                if kana_s != -1:
                    rekkyo_sw_k = kana_town[0: kana_s]
                else:
                    rekkyo_sw_k = kana_town
                if e != -1:             #行内に()が揃っている場合
                    w_town = town[s+1: e]
                    w_kana_town = kana_town[kana_s+1: kana_e]
                    k = w_town.find('〜', 0)     # ~はからで変換した文字でない
                    if k != -1:
                        w_town = ''
                        w_kana_town = ''
                    k = w_town.find('「',0)
                    if k != -1:
                        w_town = ''
                        w_kana_town = ''
                    k = w_town.find('以上',0)
                    if k != -1:
                        w_town = ''
                        w_kana_town = ''
                    k = w_town.find('以下',0)
                    if k != -1:
                        w_town = ''
                        w_kana_town = ''
                    hkana = kana_todoufuken + kana_city + rekkyo_sw_k 
                    kana  = moji.han2zen(hkana)
                    output_rtn(outf, zip, kana, todoufuken,city,rekkyo_sw,w_town)
                    rekkyo_sw   = ''
                    rekkyo_sw_k = ''
                else:
                    w_town = town[s+1: ]  #行内に(のみの場合
                    w_kana_town = kana_town[kana_s+1: ]
                    #次の行に続く
            else:
                w_town = town           # 単純な場合
                w_kana_town= kana_town
                hkana = kana_todoufuken + kana_city + rekkyo_sw_k + w_kana_town
                kana  = moji.han2zen(hkana)
                output_rtn(outf, zip, kana, todoufuken,city, rekkyo_sw, w_town)
        else:
            if e != -1:                 #)のみの場合
                w_town = w_town + town[0: e]
                k = w_town.find('〜',0)
                if k != -1:
                    w_town=''
                    w_kana_town=''
                k = w_town.find('「',0)
                if k != -1:
                    w_town = ''
                    w_kana_town=''
                k = w_town.find('以上',0)
                if k != -1:
                    w_town = ''
                    w_kana_town = ''
                k = w_town.find('以下',0)
                if k != -1:
                    w_town = ''
                    w_kana_town = ''
                hkana = kana_todoufuken + kana_city + rekkyo_sw_k
                kana  = moji.han2zen(hkana)
                output_rtn(outf, zip, kana, todoufuken,city, rekkyo_sw, w_town)
                rekkyo_sw   = ''
                rekkyo_sw_k = ''
            else:                       #()が無い場合
                w_town = w_town + town
                w_kana_town = w_kana_town+ kana_town
    inf.close()
    outf.close()


if __name__ == '__main__':
    print('makpostaldic.py開始')
    main_rtn(sys.argv)
    print('makpostaldic.py終了')