[VC2005] 郵便番号から住所を検索する方法

◆概要

 日本郵便が無償で公開しているCSV形式の郵便番号データを使って、入力された郵便番号から住所を検索してテキストボックスに表示する方法を紹介します。


 この郵便番号データに関して、日本郵便は著作権を主張していません。従って、アプリケーションに同梱することができます。

◆詳細

 このプログラムは、StringクラスのReadLineメソッドで1行ずつ読み込み、入力された郵便番号と一致するものを検索します。


フォームにラベルを2つ、テキストボックスを2つ(コントロール名:textBox1,textBox2)とコマンドボタン(コントロール名:button1)、statusStrip(コントロール名:statusStrip1)を配置してください。


フォームの設計


 また、日本郵便の郵便番号のダウンロードページから「全国一括」というデータをダウンロードしてください。配布ファイルはLZH形式で圧縮されています。解凍ソフトを使って解凍(展開)し,KEN_ALL.CSVというファイルを取り出し、Cドライブのルートに置いてください。


全角文字を半角文字にするために、Microsoft.VisualBasic名前空間の
StrConv関数を利用しています。このため、メニュー[プロジェクト]-[参照の追加]でMicrosoft.VisualBasicを選択してください。

リスト1
using System.IO;             //for StreamReader
using System.Diagnostics;    //for Stopwatch
using Microsoft.VisualBasic; //for StrConv

        private void button1_Click(object sender, EventArgs e)
        {
            string Address;         //住所
            Boolean blnFlag = false;  //見つかったかどうかのフラグ

            //処理時間を計測
            Stopwatch sw = new Stopwatch();
            //処理時間計測開始
            sw.Start();

            //郵便番号が入力されていないとき
            if (textBox1.Text == "")
            {
                MessageBox.Show("郵便番号が入力されていません。");
                this.textBox1.Focus();
                return; //処理を抜ける
            }
            //マウスカーソルを砂時計にする
            Cursor.Current = Cursors.WaitCursor;
            string sKey = textBox1.Text;
            //文字列の前後のスペースをとる
            sKey = sKey.Trim(' ');
            //Microsoft.VisualBasic名前空間のStrConv関数を使って、
            //全角文字を半角文字に変換
            sKey = Strings.StrConv(sKey, VbStrConv.Narrow, 0);
            // 文字列の長さを取得する
            int iLength = sKey.Length;
            if (iLength == 8) //"-"が含まれている
            {
                // 先頭文字目の後から '-' を検索し、見つかった位置を取得する
                int iFind = sKey.IndexOf('-', 0);
                //左から3文字+"-"文字以降をtmpZip変数に代入
                sKey = sKey.Substring(0, 3) + sKey.Substring(iFind + 1);
            }
            try
            {
                //StreamReaderオブジェクトの作成
                StreamReader sr = new StreamReader("C:\\KEN_ALL.CSV",
                                                Encoding.Default);
                //1行ずつ読み込み
                string dat;
                while ((dat = sr.ReadLine()) != null)
                {
                    string tmpZip;

                    //カンマで区切られた文字列を取得
                    string[] sbuf = dat.Split(',');
                    //配列の3番目が郵便番号
                    tmpZip = sbuf[2].Trim('"');

                    //入力された郵便番号と比較
                    if (sKey == tmpZip)
                    {
                        //住所を作成
                        //都道府県名+市区町村名+町域名
                        Address = sbuf[6].Trim('"') +
                                  sbuf[7].Trim('"') +
                                  sbuf[8].Trim('"');

                        sw.Stop();  //処理時間計測終了
                        // 処理時間をTimeSpan構造体で書式付き表示
                        TimeSpan ts = sw.Elapsed;
                        // 出力例:00:00:00.9984668
                        toolStripStatusLabel1.Text = ts.ToString(); 
                        //テキストボックスに住所を表示
                        textBox2.Text = Address;
                        blnFlag = true; //フラグをTrueにして
                        break;          //ループを抜ける
                    }
                    Application.DoEvents();
                }
                //ファイルを閉じる
                sr.Close();
            }
            catch(Exception ex)
            {
                //ファイルエラーが発生
                MessageBox.Show(ex.Message,"ファイルエラー",
                               MessageBoxButtons.OK,
                               MessageBoxIcon.Error);
                return; //処理を抜ける
            }            
            finally
            {
                //マウスカーソルをデフォルトにする
                Cursor.Current = Cursors.Default;
                
            }
            if (blnFlag == false)
            {
                MessageBox.Show("該当の郵便番号は見つかりませんでした。",
                                "郵便番号検索",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
        }

◆実行結果

  テキストボックスに「9071801」(KEN_ALL.CSVの最終データ)を入力し、コマンドボタンをクリックすると、次のように表示されます。

実行結果

 Stopwatchクラスを使って、ステータスバーに処理時間を表示しています(0.7秒)。これはF6キーを押して「ソリューションのビルド」を行ったexeファイルでの実行時間です。Visual Basic 6.0では、同じマシン(マシン環境:Pentium4 2.53GHz、1.5GB RAM)で4.4秒かかります。.NETはアプリケーションの起動は遅いのですが、このように処理は高速のようです。


(参考) 速度比較(10回平均)
  Celelon2.4GHz
224MB RAM
Pentium4 2.53GHz
1.5GB RAM
.NET C# 2005 1.5秒 0.8秒
Visual Basic 6.0 8.4秒 4.4秒

 Stopwatchクラスについては、アットマーク・アイティの処理時間を正確に計測するには?を参照してください。


 Visual Basic 6.0で同じ処理を行うには、郵便番号から住所を表示する方法を参照してください。



上記コードで基本的にはプログラムは完成ですが、少し拡張してみましょう。

◆テキストボックスの入力を制限する

 

 郵便番号は数値とハイフォンで構成されており、これ以外のアルファベットやカタカナ、ひらがなが入力できないようにしてみましょう。


 テキストボックスの入力を制限するには、テキストボックスのValidatingイベントハンドラで行います。このイベントは、コントロールの検証を行う際に発生します。

 たとえば、マウスをテキストボックス上に移動して、そこに文字を入力し、次にマウスをテキストボックス以外に移動しようとした場合、次のような順にイベントが発生します。


Enter → GotFocus → Leave → Validating → Validated → LostFocus



この Validating イベントで、入力された情報が有効かどうか検証 (Validate) することができます。


では、Validating イベントハンドラで、エラープロバイダーにエラーを表示するようにしてみましょう。

 まず、ErrorPrividerコンポーネントをフォームに追加します。

ErrorPrivider コンポーネントは、[コンポーネント] タブの [ツールボックス] で選択できます。ErrorPrivider をフォームに追加するには、ErrorPrivider コンポーネントをフォームにドラッグします。このコンポーネントはコンポーネント トレイに表示され、プロパティが [プロパティ] ウィンドウに表示されます。

errorProvider


次にtextBox1のValidatingイベントハンドラに次のコードを実装します。

using System.Text.RegularExpressions;//追加

        private void textBox1_Validating(object sender, CancelEventArgs e)
        {
            Regex regex = new Regex(@"^[0-9]{3}-[0-9]{4}$");
            if (!regex.IsMatch(((TextBox)sender).Text))
            {
                this.errorProvider1.SetError((TextBox)sender, "郵便番号");
                e.Cancel = true;
            }
            else
            {
                // エラープロバイダーのエラーをクリア
                this.errorProvider1.SetError( (TextBox)sender, null );
            }
        }

 下図のように数値とハイフォン以外の文字を入力すると、エラープロバイダーによってエラーがあることが表示されます。

 コード中では、e.Cancel=trueがミソで、これを記述すると、正しく入力されいないときは次のコントロールに行くことができません。


実行結果



◆KEN_ALL.CSVデータファイルを整理して高速化する


 KEN_ALL.CSVは、12万2,388件のデータを含んでおり(2007年11月30日版)、ファイルの大きさは11.5Mバイトあります。このため、ファイルの読み出しに時間がかかります。このプログラムでは、郵便番号と住所以外は使いません。したがって、KEN_ALL.CSVに含まれている、旧郵便番号や都道府県名や市区町村名のカタカナ表記は必要ありません。これらを事前に削除して整理しておくことにより、ファイルサイズが小さくなり検索処理も速くなります。ここでは、以下のように7桁の郵便番号と住所のみを含むyubin.txtを作成するプログラムを掲載しておきます。

 このコードを実行することによって作成されるyubin.txtは、5.05Mバイトになります。

0600000 北海道札幌市中央区
0640941 北海道札幌市中央区旭ケ丘
0600041 北海道札幌市中央区大通東

using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
// (省略)

        private const string INPUT_FILENAME = "C:\\temp\\KEN_ALL.CSV";
        private const string OUTPUT_FILENAME = "C:\\temp\\yubin.txt";

        private void button1_Click(object sender, EventArgs e)
        {
            // 処理時間を計測する
            Stopwatch stw = new Stopwatch();
            stw.Start();

            try
            {
                //ファイルの読み込み
                string[] InputString =
                    System.IO.File.ReadAllLines(
                    INPUT_FILENAME, Encoding.Default
                    );
                StringBuilder sb = new StringBuilder();

                using (StreamWriter sw
                    = File.CreateText(OUTPUT_FILENAME))
                {

                    foreach (string CurrentString in InputString)
                    {
                        // カンマで区切られた文字列を取得
                        string[] sArray = CurrentString.Split(',');
                        sb.Length = 0; // 初期化
                        sb.Append(sArray[2]);
                        sb.Append(" ");
                        sb.Append(sArray[6]);
                        sb.Append(sArray[7]);
                        if (sArray[8].IndexOf("以下に掲載がない場合") < 0)
                        {
                            sb.Append(sArray[8]);
                        }
                        sb.Replace(@"""", "");
                        sw.WriteLine(sb.ToString());
                    }
                }
            }
            catch (FileLoadException)
            {
                MessageBox.Show(INPUT_FILENAME + "の読み込みに失敗しました。");
            }
            finally
            {
                stw.Stop();
                MessageBox.Show(string.Format("処理を終了しました。{0}.{1}秒",
                    stw.Elapsed.Seconds, stw.Elapsed.Milliseconds));
            }
        }



▼ページトップへ