Byteの単位が日本語圏では異なる

Left関数とLeftB関数のように、Bが付かない関数と付く関数が用意されているものがあります。

一般的には、Bが付く関数はバイト単位である、という説明がされているのですが、これは日本においては間違いです。

Microsoftのヘルプにもバイト数と書いてありますが、もちろんそれも間違っています。Microsoftのヘルプに間違いが多いのは英語の直訳によることが多いのですが、このB付き関数も同様です。

この間違いにより、日本中のVBAプログラマーが頭をひねることに繋がっています。


日本ではバイト単位ではなく文字数×2が正しい

日本においてBが付く関数はバイト単位ではなく文字数×2の単位になります。これは日本、中国、韓国特有の問題です。英語圏ではこの問題に気付くことはおそらくありません。

例えばLeftB関数を使った以下の実行結果は”12″になります。

多くのプログラマーがこの実行結果に違和感を持ちます。”1234″ではないのか?と。

この勘違いはMicrosoftのヘルプの間違いというか説明不足と、Windowsを扱う日本のプログラマーがShift-JIS(全角=2バイト、半角=1バイトという認識)に慣れていることによります。

B付きの関数はShift-JISではなく、2バイト言語やDBCS言語と呼ばれるコード体系が採用されています。

そのため、サイズ指定の引数には文字列×2を指定したり、戻り値のサイズが文字数×2だったりするのですが、当然そのような関数はそのまま使ってもほとんど使い物になりません。


VBA関数のB付きのバイト数はShift-JISではなくDBCS言語換算

「バイト数」という言葉は文字コードによって内容が異なります。Shift-JISなら全角文字は2バイトで半角は1バイト、UTF-8は半角カナは3バイトで全角は3から5バイトで半角英数は1バイト、そしてDBCSは半角全角関わらず全て1文字を2バイトで表現します。

DBCS言語(Double Byte Character Set)である日本語の場合、文字の種類が半角全角に関わらず1文字を2バイトで表現されます。半角数字の1も2バイトで表現されます。

RightB関数などのBがつく関数は「DBCSコードとしてのバイト数」を扱います。一般的にはShift-JISの全角が2バイトで半角が1バイトの「バイト数」感覚があると思いますが、それはあくまでもShift-JISの環境の場合であって、RightB関数のようなVBAでのBつきの関数の場合はDBCSコード体系であり、Shift-JISではありません。

そのため、B付き関数はDBCS言語である日本語の場合はバイト数が「DBCS言語としてのバイト数」と解釈されるため、半角も全角も2バイト扱いになります。

このことについて間違った説明をしているサイトがとても多いのですが、それはMicrosoftのヘルプの英文を日本語訳した際のDBCSの考慮漏れが原因と思われます。

英語圏のPCであれば1バイト符号化文字集合が主流のためMicrosoftのヘルプにも「バイト数」と書いていて、それは正しい内容なのですが、その英文を日本語に直訳してしまうとDBCSのことが抜けてしまっています。それが混乱の元になっていると思われます。


Shift-JIS換算のバイト数が欲しい場合

とはいえ、実際のVBAプログラムではShift-JISとしてのバイト数が欲しいことがあります。

その場合はStrConv関数で対応します。

これが基本の変換コードになります。

元となる文字列をStrConv関数で変換します。変換後の文字列はShift-JISとしてのサイズになっているため、LenB関数のようなサイズを知りたい場合は、

とします。

サイズではなく文字列として返却するようなMidB関数などの場合は再度元のコードに変換します。

いずれも「StrConv(“123あ”, vbFromUnicode)」の基本形は同じです。


2バイト文字の先頭1バイトだけを取得した場合

上で紹介したStrConv関数によるShift-JIS換算の処理を使う場合、引数に指定するサイズには注意が必要です。

例えば以下のコードは切り出しサイズを6としていますが、正しく取得できません。

5文字目のBの半分までしか取得対象になっていないため、「123A・」のような表示になります。

見た目上の問題として、Windows7以降では2バイト文字の先頭1バイトだけが切り出されるようなサイズ指定をされた場合、文字として認識できないため「・」の表示になります。Vistaまでは表示できない文字は表示しないようになっていたのですが、Windows7からは表示できない文字は「・」として表示されるようになりました。

内部的にはVistaも見えなくしているだけでデータは存在しています。

全角文字の半分だけを取得した場合はそれを無しにするようなコードを書くことは出来ますが、私はその対応方法はあまり感心しません。そもそもの問題としてサイズ指定が間違ってるのがダメ、というのがあるためです。

プログラムの作法として、このようなサイズ指定はしないようにした方がよいでしょう。

このような場合の回避方法は、元の文字列をループで普通のMid関数で1文字ずつ取り出して、その文字をShift-JISとして何バイトなのかを上の「LenB(StrConv(取得した1文字, vbFromUnicode)」で判定し、そのバイト数を利用するなどで対応できます。