さて、最新世代のコンピュータは、文字列を扱う時に、もはや半角・全角という概念を持ったバイト数単位ではなくて、単なる文字数単位で扱うようになりました。
英語圏では都合が良いでしょうが、日本では、全角=2バイト、半角=1バイトが絶対の前提条件という場合が極めて多いわけです。
SQL-Server 2008 Expressもその例にもれず、文字列の扱いを文字数単位で行います。
そこで、SQL CLRによって、文字列をバイト数で扱う関数を作ることとしました。
ちなみに、帰宅してから、ウンウン言いながらAspire 1410で作ったばかりなので、テストは十分に出来ていません。
もしこんなコードでも参考にしていただけるなら、そのあたりはご容赦下さいませ・・・。
さて、文字列関数というと、任意の位置を切り出すSubString()、右側から文字数分切り出すRight()、左側から文字数分切り出すLeft()関数の三つがメジャーですが、これら三つのバイト単位処理のプログラムを実現します。
まず、全ての関数の中心となるプライベートなコア関数、直接は外部から呼び出せない「fnc_Core_StringB()」関数を作り、全ての関数はこれをコールして動作する・・・という仕組みにします。
コア関数一つ作っておいて、あとは簡単にSQL CLRのバリエーション展開で済ませたいなと。
■バイト単位=Shift JISで文字列を扱う!!■
バイト単位ということで、通常のUnicode形式の文字列のままではどうにもならないので、文字列をShift JISのバイナリ配列に変換して操作します。
実のところ、変換するだけなら、非常に簡単にコトが済むのです。
しかし、バイト単位で絶対に忘れてはならない点は、全角の文字を半分に断ち割ってしまうケースがあるということです。
たとえば、「あいうえお」という文字列があったとして、0バイト目から2バイトを切り出すなら結果は「あ」でOKですが、これが1バイト目から2バイトなんてやってしまうと、「あ」の半分を断ち割ってスタートしてしまいますよね。こういうのがマズイのです。
全角の前半分を1バイト目、後ろ半分を2バイト目と呼称しますが、どっちかを断ち割った状態で、そのまんま変換すると、文字化けしてしまって使い物になりません。
たとえば、半角の空白(16進数で0x20)に置き換える処理をしてあげないといけないです。
しかし、Shift JISコードの恐ろしさはここからなんですよね。
Shift JISコードは、全角の1バイト目と、全角の2バイト目が、単体では識別が出来ないという全く恐怖の仕様になってます。
バイナリを先頭から1バイトづつ順番に見て行き、全角の1バイト目を示す値だったら、次に来るバイトは必ず全角の2バイト目としてペアにする・・・という見方をしないとうまくいかないのです。
全角の1バイト目を示す値というのは、16進数「0x81」から「0x9F」、または、「0xE0」から「0xFF」の範囲に該当する、ということです。
じゃ、単体でこれ見れば済む話じゃないか!!先頭からズラズラ見る必要なし!!って思いますよね。
問題は、全角の2バイト目であっても、平気でこの1バイト目を示す範囲に入って来るという点です。知らないとヤバイくらいハマる危険アリ!!
だから、先頭から順番に見ないと、うまくないのです。単体のバイトを取り上げてチェックしてもだめなんです。この事実を知った時は、結構途方に暮れますよね。
というわけで、多少強引ではありますが、今回の記事では、文字列のバイト配列と1:1で対応したステータスの配列を作って並べて使う事にしてみました。
このあたりの処理は、もはやSQL-Serverの標準関数では作る気が失せてしまうくらいなのですが、SQL CLRがあってくれて、本当に良かったー・・・としか言いようがないです。
マイクロソフトの担当者には足を向けて寝られない気持ちですね。
詳しくは、プログラムコードのスクリーンショットを見ていただきたいのですが、C#言語で関数が組める幸せを噛み締めるしかないくらい、好きなように作れますよね。
パフォーマンスも、データ内容によりますが、単純な切り出しならば1万件程度ではパフォーマンスもほとんど悪化しなかったです。単純なテストデータをパパッと作っただけなんで、限定的な話でしょうけれど。
さて、今回の関数ですが、コア関数のfnc_Core_StringB()関数は、外部から直接は呼び出せませんがも、内部にあって全ての処理を行う文字通りのコア、メインです。
任意の位置で文字列をバイト単位で切り出すSQLCLR_SubStringB()
右側から文字列をバイト単位で切り出すSQLCLR_RightB()
左側から文字列をバイト単位で切り出すSQLCLR_LeftB()
の三つは、このコア関数をささっとコールしてるだけで実現してます。
ちなみに、List
かならずobject型になり、object型から実際に使用する型への変換オーバーヘッドの存在したArrayListと違って、
でもでも、古いバージョンの.NET Frameworkではコンパイルエラーになるので注意が必要です。