Webアプリケーションの申し込み画面等で住所を入力させる場合に、郵便番号から住所を参照させたい時がよくあります。
こんな感じですね
これをJavaScript(クライアントサイド)とPHP(サーバーサイド)の組合せで作ってみます。
郵便番号のデータは、日本郵便のサイトから取得することができます。
ここでは、「読み仮名データの促音・拗音を小書きで表記するもの」の全国版を取得しました。
ダウンロードした圧縮ファイルを展開すると、csvファイルを得られます。
全国版になるとファイルサイズもかなり大きいです。展開後のファイルサイズは、18.8Mbyteありました。
中身を見ると、こんな感じになっています。
文字コードがシフトJISになっているので、エディタやnkf等を使ってUTF-8に変換してください。
郵便番号は、3カラム目、住所は、7〜9カラム目に入っているようです。
CSVファイルの必要なカラムだけ取得したい場合は、awkを使うと一発です。
$ head ./KEN_ALL.CSV | awk 'BEGIN {FS=",";OFS=","} {print $3,$7,$8,$9}'
"0600000","北海道","札幌市中央区","以下に掲載がない場合"
"0640941","北海道","札幌市中央区","旭ケ丘"
"0600041","北海道","札幌市中央区","大通東"
"0600042","北海道","札幌市中央区","大通西(1〜19丁目)"
"0640820","北海道","札幌市中央区","大通西(20〜28丁目)"
"0600031","北海道","札幌市中央区","北一条東"
"0600001","北海道","札幌市中央区","北一条西(1〜19丁目)"
"0640821","北海道","札幌市中央区","北一条西(20〜28丁目)"
"0600032","北海道","札幌市中央区","北二条東"
"0600002","北海道","札幌市中央区","北二条西(1〜19丁目)"
awkの後の、
'BEGIN {FS=",";OFS=","} {print $3,$7,$8,$9}'
の部分がawkに実行させている命令文です。
FSで入力データの区切り文字、OFSで出力データの区切り文字を指定(ここではどちらもカンマ)し、print命令で出力するカラム($3、$7、$8、$9)を指定しています。簡単ですね。
出力をファイルへリダイレクトすれば、変換内容がファイルへ出力されます。
$ cat ./KEN_ALL.CSV | awk 'BEGIN {FS=",";OFS=","} {print $3,$7,$8,$9}' > postdata.csv
データが整理されて大分すっきりしましたが、このまま使うにはまだ問題があります。
上の例にも含まれていますが、最後の列が具体的でなく、"以下に掲載がない場合"という記述がされている行があります。
また、"〜町の次に番地がくる場合"
というパターンもあります。
他にも、"大通西(1〜19丁目)"のように、丁目が複数示されている行や、"越中畑64地割〜越中畑66地"のように範囲で記述されている場合もあります。
これらは今回の目的では不要なので、"場合"で終わる場合と、"("から後ろは削除したいと思います。同様に、"〜"が含まれる場合は全体を削除します。
また、"("なしで、複数の番地が"、"で区切られたものもあるのでこれも削除します。
厳密には()の中も残しておいた方が良い場合もあるのですが場合分けが複雑になるので、ここでは削除することにします。
不都合なデータの例
"0600000","北海道","札幌市中央区","以下に掲載がない場合"
"3060433","茨城県","猿島郡境町","境町の次に番地がくる場合"
"0683161","北海道","岩見沢市","栗沢町宮村(248、339、726、780、800、806番地)"
"4280049","静岡県","島田市","276、294〜300、302〜304番地を除く」)"
"9790622","福島県","双葉郡富岡町","〔東京電力福島第二原子力発電所構内〕)"
"0660005","北海道","千歳市","3、431−12、443−6、608−2、641−8、814、842−"
それから前後の"(ダブルクォーテーション)も外しておきます。
少し処理が込み入ってきたので、今度はPerlを使ってみます。先ほどawkを使って出力したファイルを読み込ませて処理します。
$ vi convert.pl #! /usr/bin/env perl while (<>) { # 行末の改行文字を除去 chomp; # 各列の値に分割 my ($code, $pref, $city, $town) = split(/,/, $_, 4); # 前後のダブルクォーテーションを除去 $code =~ s/^"(.*?)"$/$1/; $pref =~ s/^"(.*?)"$/$1/; $city =~ s/^"(.*?)"$/$1/; $town =~ s/^"(.*?)"$/$1/; # 「(」又は「〔」から後は不要 $town =~ s/^(.*?)((|〔).*$/$1/; # 丁目が「場合」で終わる場合、カンマが含まれる場合、「〜」が含まれる場合は全て削除 if ($town =~ /^(.+に.+場合|.*、.*|.*〜.*)$/) { $town = ""; } # タブ区切りで出力 print "$code\t$pref\t$city\t$town\n"; }
ダイヤモンド演算子を使い、上で変換したcsvファイルを標準入力から読み込み、正規表現を使って問題のパターンを見つけ、処理を施して出力しています。
Perlは、正規表現を手軽に扱えるので、このようなテキスト処理にはうってつけですね。
データベースを使う場合は、ここで各カラムの内容をテーブルに追加すれば良いと思いますが、ここではタブ文字区切りで単純に出力しました。
ファイルへリダイレクトすれば結果をファイルへ出力できます。
$ chmod u+x convert.pl # 実行権を付与
$ cat postdata.csv | ./convert.pl > postdata.txt
これでgrepを使えば郵便番号に該当する住所を取得できます。
$ grep 1000013 postdata.txt
1000013 東京都 千代田区 霞が関
郵便番号のデータができたので、次にphpでこのデータを検索して結果を返す処理を作ります。
phpからgrepを呼び出してもいいのですが、せっかくなので直接読み込んでみます。
ファイルサイズが大きいので一行ずつ読み込んで検索します。
$ vi get_addr.php
<?php
$dataPath = '/path/to/postdata.txt'; // 郵便番号データのパス
$postCode = $_GET['cd']; // 郵便番号
if (!empty($postCode)) {
$fp = fopen($dataPath, 'r');
try {
while ($line = fgets($fp)) {
list($code, $pref, $city, $town) = split("\t", $line, 4);
if ($postCode == $code) {
header('Content-Type: application/json');
$data = array('code' => $code, 'pref' => $pref, 'city' => $city, 'town' => $town);
echo json_encode($data);
return;
}
}
} finally {
fclose($fp);
}
}
http_response_code(404);
?>
ファイルを1行ずつ読みながら、受け取った郵便番号に該当する行が見つかれば、その内容をJSON形式で出力しています。
これらをphpが動作する環境に置いてブラウザでアクセスすれば結果が表示されると思います。
http://localhost/get_addr.php?cd=1000013
次はこれを呼び出すHTMLとJavaScriptです。
<!doctype html>
<head>
<meta charset="utf-8">
</head>
<body>
<form>
<div>
<label for="post-code">郵便番号</label>
<input type="text" id="post-code" name="postCode" size="14">
<button type="button" id="get-addr-btn">住所取得</button>
</div>
<div>
<label for="address">住所</label>
<input type="text" id="address" name="address" size="50">
</div>
</form>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
(function () {
// 郵便番号の書式チェック
function validatePostCode(code) {
return (code && code.match(/^\d{3}-?\d{4}$/));
}
// 住所取得ボタンクリック
$('#get-addr-btn').click(function(e) {
var code = $('#post-code').val();
if (validatePostCode(code)){
code = code.replace('-', '');
$.ajax({
url: 'get_addr.php',
type: 'get',
dataType: 'json',
data: { cd: code },
}).done(function (res) {
addr = res.pref + res.city + res.town;
$('#address').focus().val('').val(addr);
});
}
});
} ());
</script>
</body>
</html>
jQqueryを使っています。
「住所取得」ボタンがクリックされたら、先ほどのphpをAjaxで呼び出し、受け取った結果を連結して住所欄に設定しています。
$('#address').val(addr); とせず、$('#address').focus().val('').val(addr); としているのは住所の入力欄にフォーカスをあててカーソルを最後に持っていくための小技です。
Ajaxの呼び出し前に一応、郵便番号の書式チェックとハイフンの除去をやっています。(php側でハイフン付きの郵便番号に対応させなかったので)
ここでは単純化のためエラー処理等は省いています。
今回はファイルから読み出して郵便番号を検索していますが、それなりにアクセスのあるサイトであればデータベースやMemcache等の仕組みを使った方が良いでしょう。
でも、巷には郵便番号から住所に変換してくれるWebサービスやJavaScriptのライブラリも既にあるので、データのメンテナンスとか考えるとそちらを使った方が良いかもしれません。