Orotund 物言わぬは腹ふくるるわざ




000102 

文字と文章の問題


 わかりきったことではありますが、VB 内部の文字コードは Unicode です。Unicode とは何か、ということはここでは端折りますが (*1)、まぁ、VB4 が出てから 4 年も経とうというのに、LenB() が偶数しか返さないのはなぜですか、という人が多いのには恐れ入ります
 前に、今は RAD で EUC な時代だから、詳しくない人が増えてもしょうがないよね。という話をしましたが、これはちょっと事情が違います。こういう疑問を持つのは、シフト JIS という、MBCS のコード体系を知ってる人なのです。つまり、途中で情報収集をやめた人です。少なくとも VB4 が出た当時は、各雑誌が、今度は Unicode だということをかなり取り上げていましたので、不勉強のそしりは免れないでしょう。ついでに言えば、Microsoft の Office 製品が VBA を採用する時もそうでしたので、まぁ、マイナス評定されても文句は言えないんじゃないでしょうか。

 混乱の原因の一つは、Win95 や Win98 という OS が、まだシフト JIS コードだということでしょう。OS と開発言語が別の文字コードを使っているというのは、なかなか不思議な状態だと思うのですが (*2)
 他に、テキストボックスやデータベースのフィールドなんかで、入力可能な長さをバイト数で指定するのか文字数で指定するのか、なんて問題もまだまだ残っています。
 という状況なので、ある文字がいわゆる「半角」なのか「全角」なのかを判定しなければならない機会というのは、中々なくなりません。
 どうすりゃいいでしょうか。

 最初に思いつくのは、Unicode の文字列をシフト JIS に変換して、以前の判定ルーチンをそのまま適用することです。StrConv() という関数があるので、あっという間です。これは割と広く使われてるんじゃないでしょうか。
 蛇足ですが、この関数は、いわゆる「半角」−「全角」の変換や、カタカナとひらがなの変換をやってくれるので、かなり便利です。

 もう一つの方法は、Unicode の特性を利用することです。
 最初に「端折る」と宣言したくせに、結局、触れてしまうことになるのですが、いわゆる「半角英数」は、先頭に 0x00(&H00) を付け加えただけで、いままでと同じコードが使えます。
 たとえば‘A’は、7bit コードでは 0x41(&H41) ですが、Unicode では 0x0041(&H0041) となります。
 つまり、「先頭が 0 でなかったら全角」と考えることができるのです (*3)
 尤も、1 文字を 2 バイトデータとして参照しなければならないので、MidB() で分解して、AscB() で整数値に置き換えることになります。具体的には、

Dim n1st As Integer, n2nd As Integer
n2nd = AscB(MidB(strTarget, 1, 1))
n1st = AscB(MidB(strTarget, 2, 1))
 となります。n1st と n2nd の順番が逆であることに注意して下さい。
 ここで、n1st が 0 であれば、strTarget の1文字目が、いわゆる半角であると考えることができます。

 AscB() なんか使わず、ダイレクトに 1 文字分を Asc() で取り出してみてはどうでしょう。1 バイト目が先に来ますから、‘A’なら 0x0041。0x00FF より大きければ「全角」と判断できそうです。面倒なので、デバッグウィンドウで確認しましょう。
Print Hex(Asc("あ"))
82A0
 はい、OK ですね。

 …。
 OK じゃない
 “あ”が 0x82A0 ってのはシフト JIS のコードじゃありませんか!
 ちょっと待てよ、おい。早速、ヘルプに当たりましょう (*4)
解説
DBCS を使用していないシステムでは、0 〜 225 の範囲の値が返されます。DBCS を使用しているシステムでは、-32768 〜 32767 の範囲の値が返されます。

メモ 文字列に含まれたバイト データを取得するには、AscB 関数を使用します。AscB 関数は、最初の文字の文字コードではなく、最初のバイト データを返します。AscW 関数は、Unicode 文字セットの文字を返します。Unicode をサポートしていないプラットフォームでは、Asc 関数と同じ文字コードを返します。
 AscW() っていう関数があるんですね。試しに、
Print Hex(AscW("あ"))
3042
 なるほど。Unicode っぽい値になりました。

 しかし、Asc() と AscW() の違いがちょっとふに落ちない。「解説」の「DBCS を使用しているシステムでは、-32768 〜 32767 の範囲の値が返されます。」が曲者なのです。
 -32768 〜 32767 ってのは、符号無し 16bit 整数で言えば 0〜65535 ですから、単に、「16bit 整数が取りうる値のどれかを返す」と言っているに過ぎません。これだけでは、Asc() がシフト JIS を返すのか、Unicode を返すのかを判断できないのです。
 「DBCS を使用しているシステム」って何でしょう。“DBCS”がハイパーリンクなので開いてみると、「1 バイトまたは 2 バイトを使用して、文字を表すための文字セット。256 文字以上を表すことができます。」と出てきます。
 どうやら、シフト JIS のことのようですね。「1 バイトまたは 2 バイト」とある以上、Unicode ではありえない。JIS や区点もその定義には当てはまりますが、相手が Windows ですから、気にしなくてもいいでしょう。
 それでも、この文は「シフト JIS を使用しているシステムでは、0〜65535 の値を返します」と言っているだけですから、安心するのは危険なのですけどね。早い話が、「ヘルプ」の役割を果たしてないのです。
 実験結果から考えると、「Asc() は、システムデフォルトの文字コードを返す」ということだと思われます。他の国の VB を見てみないとなんとも言えませんが。

 さて、ここで問題があります。
 Asc() や Chr() が Unicode を扱うものだと思っていた人はいませんか。そのつもりでプログラムを組んでいた人はいないでしょうか。
 前半でさんざん人を罵倒しておいてなんですが、俺はそう思ってました。仕事で使う機会が無かったので実害はありませんが。
 でも、他の文字/文字列操作、大丈夫かなぁ。




*1:文字コードの問題については、C Magazine 6 月号 (JUN.1999 Vol.11 No.6、ソフトバンク パブリッシング) が詳しく特集しています。

*2:Java もですけどね。

*3:勿論、日本語 (と英語) しか使わない、という場合に限ります。あと、「半角カタカナ」は、先頭が 0xFF なので注意が必要です。

*4:ここでは、VB5 SP3 のヘルプを参照しています。





“Orotund”のページに戻る
ホームページに戻る

omot.otomo@nifty.ne.jp(O友)