PHPで郵便番号データを加工する

郵便局で公開されている郵便番号データって、色々とまずいところがありすぎてそのまま使うことはできないんですね。今頃になって知りました。

複数行にデータが分割されていたり、町域名に実際の住所ではないデータが混じってたり。ちゃんと郵便番号データを加工せずにそのまま使ってしまうと、こんなのとかこんなのが出てきて恥ずかしいことになる。

自分の目的は郵便番号から簡単な住所入力補助なので、最低限のデータ加工のみ行ってみました。

  • 複数行に分割された町域名のマージ。
  • 町域名に「以下に掲載がない場合」、または「の次に番地がくる場合」が含まれる場合は、町域名を消す。
  • 町域名に「一円」が含まれ、実際の町域名が「一円」ではなさそうな場合、町域名を消す。
    ※市区町村名='北安曇郡松川村', 町域名='松川村一円' のように市区町村名と同じ内容が町域名に繰り返される場合は消す。
    ※市区町村名='犬上郡多賀町', 町域名='一円' のような場合は消さない。
  • 町域名に「(」が含まれる場合、括弧以降を消す。
    ※大通西(1~19丁目) → 大通西
    ※みなとみらいクイーンズタワーA(6階) → みなとみらいクイーンズタワーA のように。

これ以降のことは、データを使うときにSQLなり何なりで処理する予定。

$path = "./KEN_ALL.CSV";
$records = load_postal_cd_csv($path);

foreach($records as $i=>$row)
{
    //消す
    if(contains($row[8], '合以下に掲載がない場') || 
        contains($row[8], 'の次に番地がくる場合')) {
        //echo $row[8].'<br />';
        $records[$i][5] = NULL;
        $records[$i][8] = NULL;
    }

    //存在する地名以外の「一円」を消す
    if(contains($row[8], '一円')) {
        $row[8] = preg_replace('/一円/', '', $row[8]);
        if(contains($row[7], $row[8])) {
            $records[$i][5] = NULL;
            $records[$i][8] = NULL;
        }
    }

    //開き括弧から後ろを消す
    $pos = mb_strpos($records[$i][8], '(');
    if($pos > 0) {
        $records[$i][8] = mb_substr($records[$i][8], 0, $pos);
    }

    //DBに登録するなり何なり。
    echo $records[$i][2].' '.$row[6].$row[7].$records[$i][8].'<br />';
}

//郵便番号CSVデータを読込む
//町域名が分割されている場合はマージする
function load_postal_cd_csv($path) {
    $records = array();

    $merge = array();
    $bracketed = FALSE;

    $fp = fopen($path, 'r');
    $ret = TRUE;
    $row = 0;
    while (($data = fgetcsv($fp, 0, ",")) !== FALSE) {
        $chouiki = $data[8];

        $row = NULL;
        $merged = FALSE;

        //括弧は出現していない
        if( ! $bracketed) {
            if( ! contains($chouiki, '(') ) {
                //括弧の無い通常の行
                $row = $data;
            } else {
                if( contains($chouiki, ')') ) {
                    //括弧の含まれる通常の行
                    $row = $data;
                } else {
                    //閉じ括弧が無い
                    $bracketed = TRUE;
                    $merge = array($data);
                }
            }
        } else {
            if( contains($chouiki, ')') ) {
                //閉じ括弧あり(ここまでをマージ)
                $bracketed = FALSE;
                $merge[] = $data;
                $row = merge_rows($merge);
                $merge = array();
                $merged = TRUE;
            } else {
                //閉じ括弧が無い
                //3行以上に分割された行
                $merge[] = $data;
            }
        }

        if($row) $records[] = $row;

        //if($merged) {
        //  echo $row[5].'<br />';
        //  echo $row[8].'<br />';
        //}
    }
    return $records;
}

//行マージ
function merge_rows($rows) {
    $prev_chouiki_kana = $rows[0][5];
    $ret = $rows[0];
    for($i=1; $i<count($rows); $i++) {
        $row = $rows[$i];
        $ret[8] .= $row[8]; //町域(漢字)をマージ

        //カナは前行と同じものが繰り返し出現することがあるようなので重複は除く
        if($prev_chouiki_kana != $row[5])
            $ret[5] .= $row[5]; //町域(カナ)をマージ
    }
    return $ret;
}

function contains($chouiki, $str) {
    $pos = mb_strpos($chouiki, $str);
    return !($pos === FALSE);
}

PHP逆引きレシピ 第2版 (PROGRAMMER’S RECiPE)

PHP逆引きレシピ 第2版 (PROGRAMMER’S RECiPE)