質問: Excelの質問です。住所から“市区町村のみ”の情報を抽出することは出来ますでしょうか。 今、東京都の東京タワー、福島県のフォレストパークあだたら、岐阜県のハリヨ公園など、住所データが5万件ぐらいあります。 東京都港区芝公園4丁目2−8 福島県安達郡大玉村玉井字長久保68 岐阜県山県市大森 ・ ・ この中から、 港区 安達郡 山県市 など、都道府県名を除いた市区町村名(区名)のみ抽出したいのです。 東京都港区 福島県安達郡 岐阜県山県市 でも構いません。 というのもおそらく都道府県名は岐阜県の山県市のみ気を付ければ、「*県」を空白に置換して消去、東京都・大阪府・京都府を置換で消去すれば、事足りるからです。 しかし市区町村の抽出は、千葉県の市川市など、「市*」を「市」に置換すると、市区町村が消えて「市」だけになってしまいますし。 「郡」は郡上市とか郡山市とか、置換して消去というやり方では市区町村名の表示がおかしくなる可能性もあります。 気を付けて少し時間をかければ置換して抽出できるのですが...「もっと効率的な方法がある!」ようでしたら、ご教授いただけますとありがたいです。 よろしくお願い致します。
回答: 以下のコードを標準モジュールに貼り付けて、extract_city サブルーチンを実行してください。 A列の住所から市区町村郡を切り出して、B列にセットします。 ``` Sub extract_city() source_col = 1 ' A列 : 住所 dest_col = 2 ' B列 : 市区町村 last_row = Cells(Rows.Count, source_col).End(xlUp).Row Set re_z = CreateObject("VBScript.RegExp") Dim re_c(7) For i = 0 To 7 Set re_c(i) = CreateObject("VBScript.RegExp") Next re_z.Pattern = "^(.{2}[都道府]|.{2,3}[県])(.+)" re_c(0).Pattern = "^(郡山市|市川市|市原市|郡上市|蒲郡市|四日市市|大和郡山市|廿日市市|小郡市|野々市市|高市郡|余市郡)" re_c(1).Pattern = "^([^郡]+郡)" re_c(2).Pattern = "^([^市]+市)" re_c(3).Pattern = "^([^区]+区)" re_c(4).Pattern = "^([^町]+町)" re_c(5).Pattern = "^([^村]+村)" re_c(6).Pattern = "^([^区]+区)" ' 東京都用 re_c(7).Pattern = "^([^市]+市)" ' 東京都用 For r = 2 To last_row s = Cells(r, source_col).Value Set mat = re_z.Execute(s) If mat.Count = 0 Then Cells(r, dest_col).Value = "都道府県が見つかりません" GoTo Continue End If Z = mat(0).SubMatches(0) s = mat(0).SubMatches(1) i_begin = 0 i_end = 5 If Z = "東京都" Then i_begin = 6 i_end = 7 End If For i = i_begin To i_end Set mat = re_c(i).Execute(s) If mat.Count > 0 Then If i <> 1 Then Cells(r, dest_col).Value = mat(0).SubMatches(0) Else ' 「郡」だけ特別扱い Set mat2 = re_c(2).Execute(s) ' 「市」を含む? If mat2.Count > 0 Then '短い方を採択 If Len(mat(0).SubMatches(0)) > Len(mat2(0).SubMatches(0)) Then Cells(r, dest_col).Value = mat2(0).SubMatches(0) Else Cells(r, dest_col).Value = mat(0).SubMatches(0) End If Else Cells(r, dest_col).Value = mat(0).SubMatches(0) End If End If GoTo Continue End If ' DoEvents Next Continue: Next End Sub ``` 五万件だと数分かかると思います。 その間にもシートを操作したいのであれば、末尾の方でコメントにしてある DoEvents を活かしてください。 コメントを外すと、完了までの時間は更に長くなりますが、処理中にもシートの操作ができます。 処理内容の説明をしておきます。 > 「郡→市→区→町→村」の順番で検索すると、エラーの発生を最小にすることができます > > 上記のロジックを参考にしました。 ただ、「郡」や「市」を市の名前に含む9件を例外扱いにしただけでは、以下のような住所が適切に扱えません。 > 山形県南陽市郡山 > 大阪府交野市郡津 > 山口県山口市小郡 > 鹿児島県鹿児島市郡元町 > > そこらあたりを、実際の住所データ(何かの回答の折に使った 9000件くらいの全国の)を使いながらロジックを調整しました。 「郡」が見つかった場合には、「市」でも探してみて、短い方を採用しています。 それでも、「奈良県高市郡」、「北海道余市郡」は正しく抽出できないので、特別扱いにしています。 対象の住所によっては、正しく処理できないものがあると思います。 コメントで紹介されている総務省の「全国地方公共団体コード」から表引きにすることもちらっと考えましたが、 * 「郡」の情報がない * 処理に時間がかかるかも と思って止めました。 後、質問では「郡」を切り出しの対象にしているのでそのような処理にしていますが、「郡」で切ってしまうと、「町」や「村」が抽出されないと思います。 入力済みの住所データ次第ですが、「○○郡△△村」のような表記になっているはずなので。 追記です。 今、テレビを見てて「野々市市」というのがあるというのを知りました。 コードに反映してます。 「総務省|電子自治体|全国地方公共団体コード」から、「市」の名称で「郡」と「市」が入るものが他にないことは確認しました。 #「郡」も例外的なのがもっとあるんだろうなあ... 追記です。 > #「郡」も例外的なのがもっとあるんだろうなあ... > > 「郡の変遷」に 2014年4月1日時点での「郡」の情報がまとめられていて、「郡」の名称に「市」が入るのは、「余市」と「高市」だけっぽいことは確認しました。 更に追記。 面白そうなので、「全国地方公共団体コード」から表引きについてもちょっと考えてみました。 「郡」の情報がないので、「○○郡」を外して「町」や「村」を抽出する前提です。 東京都の「区」と、他の「市」は良いのですが、「町」と「村」には、その前に「郡」がついているようです。 「全国地方公共団体コード」には「郡」の情報がありませんので、表引きをするには先頭一致ではなく中間一致で探すことになります。 中間一致で探す場合にネックになるのが、他の市町村を包含する名前があること。 例えば、「千葉県栄町」と「鳥取県北栄町」や「愛知県東栄町」です。 これをクリアするためには、表の方の名称が長い方から探さなくちゃいけません。 表引きの場合、ハッシュや二分探索を使うと速いんですが、それが使えず、名称の長い順に並べた表を使って線形探索(ありていに言えば、先頭から探す)。 1800近くある表なので、これは遅そうだ、と。 先頭一致で探せばそれなりに速くなると思いますが、その場合には「郡」を切り取らなくちゃいけない。 ちょっと邪魔くさいのが「郡上郡」。 2004年に「郡上市」になったらしいですが。 東京都の「区」と、「市」を先に振り分けて、「郡」を切り取れれば、残りは「町」と「村」。 名称に両方の文字を含むのは、「群馬県玉村町」と「宮城県村田町」のふたつ。 「町」で先に切り出せば良いかな、と思ったら「長崎県大町町」なんてのがある。 というわけで、市町村の一覧を利用する場合には、以下のような手順になりそう。1. 東京都で「区」を切り出す 2. 都道府県を切り取って、先頭一致で表引きで「市」を切り出す 3. 「郡上郡」を特別扱いにして、「郡」を切り取る 4. 「大町町」を特別扱いにして、「町」を切り取る 5. 「村」を切り取る 「町」、「村」の切り取りは、まとめて先頭一致の表引きでも良いでしょう(遅いけど)。 表引きでも、素直に表と照らし合わせるだけでは駄目でヒューリスティックなロジックにならざるを得なし、市区町村のリストは頻繁ではないにしても更新が入りますので使い続けるつもりのプログラムなら保守が必要。 だったら表なんか使わなくても良いかなあ、という印象です。 またまた追記。 表引きにするなら、「読み仮名データの促音・拗音を小書きで表記するもの - zip圧縮形式 日本郵便」を使う方が良さそう。 この一覧の 8列目を抽出して重複を取り除けば* 市 * 市+区 * 郡+町 * 郡+村 の完全なリストが手に入る。 全部で 1894件。 これを長い順に並べ替えて、住所から都道府県を切り取った文字列と先頭一致で比較して一致したら採用。 「郡は要らない」とか「郡の方を採用」は、一覧にある文字列を加工する感じでしょうか。 ロジックは単純になりますね。 # 総務省の「全国地方公共団体コード」の方は 1741 件で、150件ほど差があるのは廃止になったものを一部含んでいるからかな? 追記。 > # 総務省の「全国地方公共団体コード」の方は 1741 件で、150件ほど差があるのは廃止になったものを一部含んでいるからかな? > > いや、郵便番号の方は「○○市○○区」の表記だからだった。 一部の差異を除いて、ほぼ同じ。 漢字の表記の違いが2件と、「郡」ではなく「島」という表記が 2件(三宅島と八丈島)。 東京都だからだなあ、きっと。 またまた追記です。 郵便番号の一覧をベースに表引きを軽くやってみました。 性能的な問題は、表を作るときに最大の文字数を控えておき、住所から切り出すときには頭から切り取る文字数を、最大文字数からひとつずつ減らして、その度に表(Dictionary オブジェクト)にあるかどうかを確認するというふうに対処しました。 回答に書いた正規表現を使う場合と、実行時間はさほど変わらない感じです。 ただ、ちょっと問題がありまして。 手持ちの 9000件くらいのデータで試してみたんですが、250件くらいが引き当てできていません。 幾つかサンプリングして調べてみると、以下のようなパターンが。* 「町」が「市」になった * いくつかの「町」、「村」が合併して市になった * 政令指定都市になり、「区」が入った 良くも悪くも入手できる一覧は最新なので、市区町村を切り出す対象の住所に古いものが含まれると、取りこぼしがありそうです。 ぼくが試したデータでは 2.8% を取りこぼしているので、プログラムで切り出した方が例外が少ないという結果になります。 一覧表のデータは、プログラムで切り出した値の検算に使うのが良いかな、という気がしました。