20日目担当の細川です。見かけまして、自分も社会貢献してみたいな、と思いました。郵便番号データは多くのサービスにとって最も基礎的なマスタデータですが、その作成は闇に包まれていると聞きました。興味を持ったのが間違いでしたので自作にトライしてみました。生成元データは日本郵便の公式サイトにある読み仮名の促音・拗音を小書きで表記するものを使います。ここからあの有名なKEN_ALL.CSVが入手できます。このファイルは以下に記載しますようにそのままではシステム的な用途では使いにくいため、使いやすい形に変換する必要があります。手強いという評判ですから、変換用プログラムに使う言語には切れ味に定評のあるHaskellを使います。つないだものを利用しました。私はHaskell普段はあまり触らないのですが、快適な環境が簡単に手に入り感動です。変換後の郵便番号データの仕上がり具合を判定する材料として、無料で変換済みのものを配布されているzipcloudさんのものを参考にしました。こちらではapiサービスも最新のデータで提供されていて素晴らしいです。KEN_ALL.CSVは文字コードがShift-JISなのですが、Haskellでやる上で色々と面倒な予感がするため、UTF-8にしておきます。スマートにそのまま変換したいところですが、今回の趣旨の中心的なものではないので端折りました。KEN_ALL.CSVの問題点を知るzipcloudさんのサイトにKEN_ALL.CSVからの変換点として下記の記載があります。町域名で、丸括弧で囲まれている部分を除去つまり、このファイルの変換で問題になるのは町域名という、CSVの9番目の項目(半角カナは6番目)の取扱だけ、ということになります。※町域名の文字数が多いために複数行に分割されてしまっている場合は、1行にマージしておいてから丸括弧を除去します。※括弧内の文字が「地名」や「ビルの階」など、住所として使えそうなものは町域名の末尾に追加します。※括弧内に地名が複数列挙されている場合は複数行に分割します。実際に見てみると、町域名だけが下記のように複数行にまたがっているものが存在します。"協和(88-2、271-10、343-2、404-1、427-""3、431-12、443-6、608-2、641-8、814、842-""5、1137-3、1392、1657、1752番地)"つまり、このCSVファイルは1行が1データとは限らないだけではなくて、1行のCSVレコードとして完結すらしてない場合がある1行のCSVレコードは町域名に括弧が無い、あるいは、両括弧が揃うまで町域名をマージした複数行によって得られる町域名括弧内の文字列は複数の町域名を表現している場合があり、その場合は更に分割された町域名ごとに同一郵便番号の複数レコードを生成する必要があるというCSVデータとしては結構ダイナミックな取扱が必要ということになります。ここを制覇できるかどうか、つまりいかに町域名を取り扱うかがKEN_ALL.CSVの問題点と言えそうです。CSVレコードを読み込む今回HaskellでのCSVパーサーにはcassavaを使ってみました。(Generic)した上でFromRecordやToRecordのinstance宣言だけすれば、基本的な型の値については変換の実装不要になります。decodeすることもできるようです。今回は適当なフィールド名で全て取り込むことにします。Postcode今回は読み込み->変換->CSV出力をしてみたいと思いますので、メインの処理は下記のようになりました。ところが中心となる変換処理になります。郵便番号変更あった行のデータ)で、出力データの判定に使用します。records町域名に基づいたCSVレコードのマージここからが本丸です。複数行にまたがった町域名をマージして、CSVレコードとして完結したものにしてから、各種置換などの処理を行う必要があります。下記はparseされたcsvをレコードごとにfoldする関数になりますが、町域名(townArea)を見て括弧が開いて閉じてない状態の場合は次の畳込み(fold)に値を回し、次行とマージ、括弧が閉じてからencodeして出力します。出力の際には、変換後の値が前回出力行と重複するケースがあるため、比較してフィルタをするようにしています。Unicodeをサポートしています。次は町域名に従ったレコードごとの変換処理をします。町域名の変換後に、複数の町域名が生成される可能性があるので、その数分だけ町域名違いのレコードデータを生成します。後に記載しますが、町域名に何か書いてあっても町域名ではない場合があるので、その場合は町域名空文字にレコードを生成します。しまずは町域名が無しと考えるべきケース。マッチする場合には町域名無しと考えます。中央アエル(1階)といった場合は、括弧を取ってあげます。岩手県に特有らしいのですが、地割りという表記があり、穴明22地割、穴明23地割や葛巻(第40地割「57番地125、176を除く」~第45地割)という町域名が現れる場合があります。これをそれぞれ穴明、葛巻に変換します。町域名に括弧が存在し、上記のビル階層などでは無い場合は括弧内文字列の変換をします。大通西(20~28丁目)のような場合は大通西に、留萌村(峠下)のような場合は留萌村と留萌村峠下に分割します。詳細については後ほど記載します。括弧内の文字列は、最初に、を区切り文字として分割した上で、不要な文字列と思われるものを取り除きます。この時に重要なのは町域名の漢字(CSVの9番目)の項目をベースにするということです。町域名漢字の括弧内文字列が区切られていても、半角カナのほうはそうなっていない場合があります。その場合は半角カナの値をそのまま使うようにします。regex)その他それ以外はCSV記載のまま出力(変換無し)です。細かな調整は必要と思われるものの、目に付いた大きな点は対応できた気がしてきました。main関数はこんな感じで、結果はken_all.converted.csvファイルに出力されます。output結果ですが、zipcloudさんのファイルとの差分はzipcloud版行数:127,055追加:126削除:29変更:77という結果になりました。具体的に見てみましょう。※記載したzipcloudさんの出力値は私の実装との比較のためにダブルクオーテーションを消してあります。zipcloudさんのデータにはKEN_ALL.CSVと同様の仕様でダブルクオーテーションが入っています。0970035,ホッカイドウ,ワッカナイシ,バッカイムラバッカイ,北海道,稚内市,抜海村バッカイ,1,0,0,0,0,0になってしまってました。これはつまり、抜海村とバッカイは同一のものを指してるので重複である、と認識する必要があったようです。my実装01459,07102,0710245,ホッカイドウ,カミカワグンビエイチョウ,フクトミ,北海道,上川郡美瑛町,福富,1,0,0,0,0,001459,07102,0710245,ホッカイドウ,カミカワグンビエイチョウ,フクトミフクトミエイシン,北海道,上川郡美瑛町,福富福富瑛進,1,0,0,0,0,0というのもありました。これも重複であることを知ってる必要がありそうです。my実装03366,02955,0295511,イワテケン,ワガグンニシワガマチ,ウエノノ,岩手県,和賀郡西和賀町,上野々,0,0,0,0,0,0数字+地割は削除している箇所が多い気がしますが、ここは残してあるようです。my実装16211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニアラヤシキ,富山県,射水市,青井谷新屋敷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニゴカンノ,富山県,射水市,青井谷五官野,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニサンノ,富山県,射水市,青井谷三野,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニニシタニ,富山県,射水市,青井谷西谷,0,0,0,1,0,016211,93903,9390321,トヤマケン,イミズシ,アオイダニ,富山県,射水市,青井谷,0,0,0,1,0,0となってしまって、町域名が青井谷の完全に重複したレコードが複数できてしまってました。今回これは処理時に郵便番号が変わった時点の変換後行を保持して、以降の行が同一であれば出力しない、というロジックでフィルタすることができました。KEN_ALL.CSVは同一の郵便番号が全然離れた行に現れたりもするので、完全対応はDBなどに入れて処理するしかない感じがします。今回の処理の副作用で下記のような削除行が出てしまいました。これは私の実装のほうが重複無くできているように見えますが、よく見ると末尾の方の0/1フラグが微妙に違ってます。zipcloudさんのはこの辺もちゃんと見て重複除去を行っているようです。)と書いてることも多いのですけど、富山県などでは何でこうなってるんでしょうか。いや、そんな疑問を持ってはいけませんね。他にも細かな相違点はありましたが、多くは重複行が残ってる部分でしたので、それが取り除かれればかなり目標近くに行けそうです。このやっつけ仕事で127,055行の見本ファイルとの差分が240件ぐらい、そのうち追加行が126程度ということで、結構満足してしまってるのですが、業務で実際に使うとなると厳しいでしょうか。メンテのことなども考慮すると、職人さんの作ったものを利用するか、商用のものが安心ですね。ここまでやってきてふと思うのは、何で今の時代にこういうファイルが放置提供され、且つ更新し続けているのか、という点です。町域名が複数行にまたがっているということを知った時点で、このファイルそもそも大丈夫なのかな?という疑問を普通は持ってしまうでしょう。住所と郵便番号、管理されてきたシステムの歴史にまつわるものかと思われますが、どこかでキレイになって欲しいですね。それはともかく、Haskell環境は本当に昔に比べてよくなりました。型シグネチャ見るために都度hackageのドキュメント見たり、慣れないEmacsの設定したりといったことは今回不要で、快適に実装ができました。Haskellからは離れてしまってたのですが、今後は積極的に使っていこうと思います。Hosokawa@yhosokFollow株式会社オープンロジ物流はこれから、テクノロジーによりダイナミックに変化する業界です。物を作る人とそれを欲しい人、その間の物流や配送がすべてネットワーク化された、需要と供給が最適化される次世代のインフラづくりを私たちは目指しています。upLoginユーザーは見つかりませんでした。