Version 11.04
文字種の判定
「前回は、日本語の文字列、リードバイトとトレイルバイトについて見てみ
ました」
『なんかすんごくこんがらがっちゃったんだけど……』
「そうだね、ここはわかりにくいと思うからもう一度見ておこうか。まず、
日本語の文字は2バイトで1文字を表します。たとえば、ウィンドウズで使
われている文字コードの Shift JIS だと、カタカナのソは」
ソ
83 5C
「で表します。で、これがウィンドウズの画面に表示されるときには」
[「ソ」を表す数字 0x83 0x5C ]
↓
[ Shift JIS 文字コード表で数字を「ソ」に]
↓
[フォントの「ソ」を使って画面に表示]
「って手順になります」
『でも』
\
5C
『って、これがソのあとのバイト……トレイルバイトって言うんだよね』
「そう、最初のバイトがリードバイト、後のバイトがトレイルバイト。だか
ら」
83 : リードバイト
5C : トレイルバイト
「ってなります」
『そう、そのトレイルバイトが \ と同じ数字なんだよねー』
「そう。英数字……前に言ったかな、 ASCII 文字」
『えーっと…… Version 5.04 ( No.069 ) でやったね』
「そう。英数の文字コードは ASCII 文字コードって言います。それと、
Shift JIS のリードバイトとトレイルバイトはこうなります」
ASCII 文字: 0x20 〜 0x7F
リードバイト: 0x81 〜 0x9F 、 0xE0 〜 0xFC
トレイルバイト: 0x40 〜 0x7E 、 0x80 〜 0xFC
『あれ、この前は英数の方は見なかったよね』
「そっちも重要だからね。 ASCII とリードバイトは重なっていないけど、
トレイルバイトとは……」
『あ、重なってる!!』
「 \ も英数の一種だから」
『ソのトレイルバイトと同じになっちゃったわけね……』
「そして、リードバイトとトレイルバイトも重なってます」
『それが前回の=よね』
「そう。=は」
=
81 81
「って文字コードだから」
『これじゃリードバイトとトレイルバイトがわかんないよね』
「さっきの ASCII 文字とトレイルバイトもね」
void OutputLeadAndTrail()
{
char ch[] = "ソ";
TRACE( "%s\n%s\n", ch, &( ch[1] ) );
}
『ビルドして実行!』
ソ
\
『ってなるわけよねー』
「これはとても重要な点なんです。さっきの=もだけど、ウィンドウズでさ
えも、いきなり 0x5C を渡されたら、〈ソのトレイルバイト〉って判らない
ってこと」
『ええっ!? じゃあどうやって調べるの??』
「種明かしをすると、 ASCII 文字とリードバイト、トレイルバイト、この
3つには次の法則が成り立つんです」
・トレイルバイトは必ずリードバイトの次に来る。
・文字列の最初にトレイルバイトが来ることはない。
・トレイルバイトの次にトレイルバイトが来ることはない。
『……あたりまえじゃない』
「もちろんそうなんだけどね。でもこれを使えば、必ず文字種を判定できる
んです。たとえば」
\
5C
「これは1文字目だからトレイルバイトじゃない」
『から \ なわけね』
ソ
83 5C
「これは、まず 0x83 はリードバイトにもトレイルバイトにもある文字コー
ドだけど、1文字目だからリードバイト」
『うんうん』
「そのリードバイトの次に来る 0x5C は絶対にトレイルバイトだから、この
0x5C が \ って見られることはなくて、だからこの2バイトはソって見なさ
れるんです」
『なんかちょっとパズルっぽい』
「かもね。これを踏まえて」
C:\ソフト.txt
「を先頭からソまで調べると」
C : 0x43 : 1文字目だからトレイルバイトじゃないので ASCII 文字。
: : 0x3A : 問答無用で ASCII 文字。
\ : 0x5C : ひとつ前の : が ASCII だから次にトレイルバイトが来ること
はないから、これは ASCII 文字。
ソ(1バイト目) : 0x83 :
ひとつ前の \ が ASCII 文字だからトレイルバイトには成り得
ない、だからリードバイト。
ソ(2バイト目) : 0x5C :
ひとつ前がリードバイトだからトレイルバイト確定。
「ってなります。だから、このソの2バイト目は \ じゃなくてソのトレイ
ルバイトってことがわかるんです」
『そっか、ようするに最初からひとつひとつ見ていけばいいってことね。
って、それってかなり面倒じゃん!!』
「面倒なんだけど、それでもこうしないとわからないからねー」
『……で? これ、プログラムでやるの?』
「微妙。とりあえず、 _mbsbtype() って関数があるからまずはこれを使っ
てみます」
void OutputCharType()
{
unsigned char ch[] = "\\ソ";
TRACE( "%d\n", _mbsbtype( ch, 0 ) );
TRACE( "%d\n", _mbsbtype( ch, 1 ) );
TRACE( "%d\n", _mbsbtype( ch, 2 ) );
}
『?』
「 _mbsbtype() は、第1引数の文字列の、第2引数文字目の種類を返しま
す。実行するとこんな感じ」
0
1
2
『ぜろいちに?』
「これは _mbsbtype() のリファレンスを見てみて」
『えーっと、 0 がシングルバイト文字……?』
「1バイトの文字、つまり ASCII 文字ってこと」
『あー。 1 がマルチバイト文字の先行バイト……?』
「マルチバイト文字っていうのは複数のバイトで表現される文字のこと。
まぁようするに Shift JIS の文字ってこと」
『先行バイトは……あ! リードバイトのこと!』
「そういうこと。 2 はマルチバイト文字の後続バイト」
『トレイルバイトってことね。おおっ、これ使えばちゃんとわかるじゃな
い!!』
「一応ね」
『一応?』
「まず、この関数って最初に _ が付いてるでしょ。こういう種類のランタ
イムは VC にしか入ってないランタイムなんです」
『ってことは他の……他の、なんだろう』
「まぁ VC 以外は当分ないだろうからそれは考えない方がいいか。じゃ、実
際に、 Version 11.02 ( No.202 ) のファイル名を取り出す関数を改良しま
す」
void OutputFileName2()
{
char ch[] = "C:\\Test\\ソフト.txt";
char chOut[MAX_PATH];
int iCharType = _mbsbtype( (unsigned char *)"\\", 0 );
// 後ろから見ていきます。
for( int iIndex = strlen( ch ) - 1; 0 <= iIndex ; iIndex-- )
{
// 文字種も含めてチェック。
if(
( ch[iIndex] == '\\' ) &&
( _mbsbtype( (unsigned char *)ch, iIndex ) == iCharType )
)
{
// \\ がみつかりました。
// その次の文字から最後までコピー。
strcpy( chOut, &( ch[iIndex + 1] ) );
break;
}
}
// 出力します。
TRACE( "%s\n", chOut );
}
『そっか、文字コードそのものだけじゃなくて、 _mbsbtype() の文字種も
一致したら、みつかったってすればいいんだね』
「そういうこと。実行すると」
ソフト.txt
『おおっ!! こうすればちゃんと調べることができるんだね』
「 _mbsbtype() はこの手の問題の特効薬ってくらい便利な関数だから」
『はーい。……あれ? そういえば unsigned って付いてるのはなんで?』
「それについては次回で」
/*
Preview Next Story!
*/
『今回はホントにパズルっぽかったね』
「ま、それもプログラムを作る楽しみのひとつかも」
『えー?』
「普通、パズルって問題毎に解くけど、プログラムで作っちゃえば」
『それがどんな問題も解いてくれる……?』
「ってほどうまくいかないんだけどね」
『というわけで次回』
< Version 11.05 signed char と unsigned char >
「につづく!」
『って前にやったじゃんそれ』
「こういうのを知らないと、パズルを解けたと思ったら……」
『思ったら……?』