SJISの扱い方

そんなわけで、フォント表示を作っていたときに、SJISコードについて調べたので、作業記録を残しておきます。

SJISコードでは、文字列を先頭から読み込んだ時の1byte目には[0x81,0x9f]の範囲と、[0xe0,0xef]の範囲の値が入ります。
そして、続く2byte目には、[0x40,0xfc]の範囲の値が入ります。
ただし、2byte目の範囲には、0x7fは含まれません。

また、asciiコードの範囲は[0x20,0x7e]、半角カナコードの範囲は[0xa1,0xdf]となります。

(ここで、[x,y] の表記は、x以上、y以下の範囲(x,yどちらも含める)としました)

まとめますと、

asciiコードの範囲 [0x20,0x7e]
半角カナコードの範囲 [0xa1,0xdf]
SJISコードの1byte目 [0x81,0x9f] もしくは [0xe0,0xef]
SJISコードの2byte目 [0x40,0xfc]

となります。

さて、SJISで表現されたchar配列から、文字入力ストリームを作ることを考えて見ることにします。
char配列を読み込みながら、一文字分の文字を返してくれるようなストリームです。

例えば、こんな感じ...

char a_str[] = "あいうえおacdeカキクケコ";

CharStream a_cs( a_str, strlen( a_str ) );

char a_buf[3];
while( a_cs.ReadNext( a_buf ) ){
    printf( "%s\n", a_buf );
}

出力結果

あ
い
う
え
お
a
b
c
d
カ
キ
ク
ケ
コ

重要な部分だけの、擬似コードを書いてみると

bool IsSJISReaderByte( char i_c )
{
    int code = i_c;
    if( (0x81 <= code) && (code <= 0x9f) || (0xe0 <= code) && (code <= 0xef) ){
        return true;
    }
    return false;
}

bool CharStream::ReadNext( char* i_output )
{
    if( IsFail() ){
         return false;
    }

    char a_c = readByte();

    if( IsSJISReaderByte( a_c ) ){
         i_output[ 0 ] = a_c;
         i_output[ 1 ] = readByte();
         i_output[ 2 ] = '\0';
    }else{
         i_output[ 0 ] = a_c;
         i_output[ 1 ] = '\0';
    }

    return true;
}

こんな感じになります

逆に、SJISコードを全部出力したい場合には、

void printSJISChar()
{
    char a_buf[3];
    for( u16 a_hi = 0x81; a_hi <= 0x9f; a_hi++ ){
        for( u16 a_low = 0x40; a_low <= 0xfc; a_low++ ){
            if( a_low != 0x007f ){
                a_buf[0] = a_hi;
                a_buf[1] = a_low;
                a_buf[2] = '\0';
		printf( "%s", a_buf );
            }
        }
    }
}

こんな感じにダンプすれば良いでしょう。

ま、文字コードの範囲さえわかっちゃえば、やることは簡単ですよね