2011/3/4 - 2011/3/19
Pen Jr.D言語なんてやってみそWelcome Beginner補足
文字化け
D言語 で コンソールアプリ を組む場合、日本語の文字化け問題を解決する必要があります。

「日本語表示を諦める」という解決方法もありだと思いますが、日本語表示が出来ない コンソールアプリ ではさすがに面白みに欠けます。

とりあえず toMBSz で変換して済ますという安直な方法を使っていますが・・・
文字コード
文字列 の表示には、それぞれの 文字 の形を表示しますから、多彩な表示のためにはより多くの 文字キャラクタ が必要になり、その 割り当て を決める必要もあります。

文字コード というのは 文字キャラクタコード のことで、その 割り当て を決めた 規格 そのものを指したりもします。

ASCIIコード
0 から 0x7F( 0 - 127 )の値に英数字、記号及び制御コードを割り当てた規格。

もちろん 英数字と記号にも ASCIIコード 以外の規格はありますし、汎用コンピューターでなければ独自のコード割り当てもありです。

また、ASCIIコード は 7ビット に収まっていて 1バイト の後ろ半分( 0x80 から 0xFF )が未使用なので、ASCIIコード はそのままに 未使用部分だけ を使って拡張しているのが UTF-8 などの文字コードです。

「ASCIIコード表」を検索すればありますが、制御コード以外を見るのは簡単です。

体裁をいくらか考えて、

import std.stdio;

  /*----------------------------
  *   数値 n を 整形して出力
  *
  *   10進数( 16進数 ) : 文字
  */
void toAscii( in int n, in string s = "  " ){
  // 0 - 0x1F の制御コードの表示は NG
  writef( "%s%3d( 0x%02x ) : %s", s, n, n, cast( char )n );
}

  /*****************************
  *
  *   ASCII コード表
  */
void main(){
  writeln(
    "    ---- ASCII Code ----\n"
    "  0 - 31( 0x1F ) : control code"
  );
  foreach( i; 0x20 .. 0x80 ){
    if( i % 4 ){
      toAscii( i );
    }
    else{
      toAscii( i, "\n" );
    }
  }
  writeln();
}
D:\Pen-Jr\My Programs\supp>test
    ---- ASCII Code ----
  0 - 31( 0x1F ) : control code

 32( 0x20 ) :     33( 0x21 ) : !   34( 0x22 ) : "   35( 0x23 ) : #
 36( 0x24 ) : $   37( 0x25 ) : %   38( 0x26 ) : &   39( 0x27 ) : '
 40( 0x28 ) : (   41( 0x29 ) : )   42( 0x2a ) : *   43( 0x2b ) : +
 44( 0x2c ) : ,   45( 0x2d ) : -   46( 0x2e ) : .   47( 0x2f ) : /
 48( 0x30 ) : 0   49( 0x31 ) : 1   50( 0x32 ) : 2   51( 0x33 ) : 3
 52( 0x34 ) : 4   53( 0x35 ) : 5   54( 0x36 ) : 6   55( 0x37 ) : 7
 56( 0x38 ) : 8   57( 0x39 ) : 9   58( 0x3a ) : :   59( 0x3b ) : ;
 60( 0x3c ) : <   61( 0x3d ) : =   62( 0x3e ) : >   63( 0x3f ) : ?
 64( 0x40 ) : @   65( 0x41 ) : A   66( 0x42 ) : B   67( 0x43 ) : C
 68( 0x44 ) : D   69( 0x45 ) : E   70( 0x46 ) : F   71( 0x47 ) : G
 72( 0x48 ) : H   73( 0x49 ) : I   74( 0x4a ) : J   75( 0x4b ) : K
 76( 0x4c ) : L   77( 0x4d ) : M   78( 0x4e ) : N   79( 0x4f ) : O
 80( 0x50 ) : P   81( 0x51 ) : Q   82( 0x52 ) : R   83( 0x53 ) : S
 84( 0x54 ) : T   85( 0x55 ) : U   86( 0x56 ) : V   87( 0x57 ) : W
 88( 0x58 ) : X   89( 0x59 ) : Y   90( 0x5a ) : Z   91( 0x5b ) : [
 92( 0x5c ) : \   93( 0x5d ) : ]   94( 0x5e ) : ^   95( 0x5f ) : _
 96( 0x60 ) : `   97( 0x61 ) : a   98( 0x62 ) : b   99( 0x63 ) : c
100( 0x64 ) : d  101( 0x65 ) : e  102( 0x66 ) : f  103( 0x67 ) : g
104( 0x68 ) : h  105( 0x69 ) : i  106( 0x6a ) : j  107( 0x6b ) : k
108( 0x6c ) : l  109( 0x6d ) : m  110( 0x6e ) : n  111( 0x6f ) : o
112( 0x70 ) : p  113( 0x71 ) : q  114( 0x72 ) : r  115( 0x73 ) : s
116( 0x74 ) : t  117( 0x75 ) : u  118( 0x76 ) : v  119( 0x77 ) : w
120( 0x78 ) : x  121( 0x79 ) : y  122( 0x7a ) : z  123( 0x7b ) : {
124( 0x7c ) : |  125( 0x7d ) : }  126( 0x7e ) : ~  127( 0x7f ) : 
Unicode
英数字と記号だけでなく、世界中の文字を符号化( 数値化 )する規格。

16ビット( 0 - 0xFFFF )に納めた規格( UTF-16 )と、32ビット( 0 - 0xFFFF_FFFF )の規格( UTF-32 )がありますが、いずれも各々の国の文化などはほぼ考慮されてはいませんから、最近流行の書籍のデジタルが Unicode なのは問題でしょうが、普通問題になるような欠落があるわけではないと思います。

UTF-8
Unicode 文字 を 可変長のバイト値( 8ビット値 )に変換する規格で、

日本語の文字の多くは 3バイト 以上になりますから日本語の文章などでは不利ですが、ASCII 文字 は 1バイト で済みますから、プログラムコード などの英数字や記号が多いテキストには有利。

また、可変長 と言うことで UTF-16 より表現力があるらしいです。

UTF-16
全て 2バイト( 16ビット )の規格。

Windows( Win32API )の Unicode は UTF-16 です。

UTF-8Shift-JIS
Unicode な D が 日本語で文字化けするのは Shift-JIS との違いが原因なわけですが、

比較すると、

import std.stdio, std.windows.charset, std.conv;

  /*****************************
  *   文字列 s のコード表示
  */
void dispArray( in string nam, in string s ){
  writef( "%s = [\n  ", nam );
  
  foreach( i, c; s ){
    if( i ){
      // 一行の幅を制限する
      if( i % 10 ){
        write( ", " );
      }
      else{
        write( "\n  " );
      }
    }
    // charをubyteに変換して16進数表示する
    writef( "0x%02x", cast( ubyte )c );
  }
  writeln( "\n  ]" );
}

  /*****************************
  *
  *   引数文字列のエンコード比較
  */
void main( in string[] args ){
  foreach( i, s; args[ 1 .. $ ] ){
    if( i ) writeln();
    // Shift-JIS に変換
    auto sj = to!string( toMBSz( s ) );
    writefln( "    %s( %s )", sj, s );
    
    dispArray( "UTF-8", s );
    dispArray( "Shif-JIS", sj );
  }
}
D:\Pen-Jr\My Programs\supp>test abcABC012!@# D言語の文字化け

    abcABC012!@#( abcABC012!@# )
UTF-8 = [
  0x61, 0x62, 0x63, 0x41, 0x42, 0x43, 0x30, 0x31, 0x32, 0x21
  0x40, 0x23
  ]
Shif-JIS = [
  0x61, 0x62, 0x63, 0x41, 0x42, 0x43, 0x30, 0x31, 0x32, 0x21
  0x40, 0x23
  ]

    D言語の文字化け( ・、險€隱槭・譁・ュ怜喧縺・)
UTF-8 = [
  0xef, 0xbc, 0xa4, 0xe8, 0xa8, 0x80, 0xe8, 0xaa, 0x9e, 0xe3
  0x81, 0xae, 0xe6, 0x96, 0x87, 0xe5, 0xad, 0x97, 0xe5, 0x8c
  0x96, 0xe3, 0x81, 0x91
  ]
Shif-JIS = [
  0x82, 0x63, 0x8c, 0xbe, 0x8c, 0xea, 0x82, 0xcc, 0x95, 0xb6
  0x8e, 0x9a, 0x89, 0xbb, 0x82, 0xaf
  ]

なぜ コマンドライン引数 が UTF-8 なのかいまいちよくわかりませんが、Shift-JISUTF-8 では ASCIIコード 部分は同じ様ですが、日本語コードにはかなりの違いがあります。

特に サイズ がかなり違うのというのはどうゆうこちゃねん、と

Shift-JIS が糞(確)
サイズがコンパクトで優秀なふりをするのはその経緯を見ればいかにもな感じですが、何気に選んだ日本語にもすでにあるはずがないコードがあったりします。

ASCIIコード というのは 0 から 0x7F に英数字と記号、制御コードが割り振られていて、0x80 から 0xFF は使っていませんから、ASCIIコード が同じで、その未使用領域だけを使って拡張していれば ASCIIコード の解析には問題がないのですが、

Shift-JIS は、

import std.stdio, std.windows.charset, std.conv;

  /*****************************
  *   c が ASCIIコードなら
  *     その文字を返す
  */
char isAscii( in char c ){
  if( c < 0x80 ) return c;
  return ' ';
}

  /*****************************
  *     文字列 s のコード表示
  */
void dispArray( in string s ){
  writef( "%s = [\n  ", s );
  
  foreach( i, c; s ){
    if( i ){
      if( i % 6 ){
        write( ", " );
      }
      else{
        write( "\n  " );
      }
    }
    // 文字コード表示とASCIIコードチェック
    writef( "0x%02x( %s )", cast( ubyte )c, isAscii( c ) );
  }
  writeln( "\n  ]" );
}

  /*****************************
  *
  */
void main( in string[] args ){
  foreach( i, s; args[ 1 .. $ ] ){
    writeln();
    dispArray( to!string( toMBSz( s ) ) );
  }
}
D:\Pen-Jr\My Programs\supp>test abcABC012!@# 暴力 表示+

abcABC012!@# = [
  0x61( a ), 0x62( b ), 0x63( c ), 0x41( A ), 0x42( B ), 0x43( C )
  0x30( 0 ), 0x31( 1 ), 0x32( 2 ), 0x21( ! ), 0x40( @ ), 0x23( # )
  ]

暴力 表示+ = [
  0x96(   ), 0x5c( \ ), 0x97(   ), 0xcd(   ), 0x81(   ), 0x40( @ )
  0x95(   ), 0x5c( \ ), 0x8e(   ), 0xa6(   ), 0x81(   ), 0x7b( { )
  ]

英数字だけならまだしも、記号まで使ってやがります。

UTF-8 表示のところに Shift-JIS を渡すと、文字化け 程度では済まないというわけで、特に 解析 は間違えなく失敗します。

要するに、Shift-JIS に変換してから、UTF-8 を扱う Phobos に渡すのは 間違い なわけなのですが、
糞を渡しても転けない?
std.utfPhobos
エンコード
string toUTF8( in char[] s ); 
string toUTF8( in wchar[] s ); 
string toUTF8( in dchar[] s );
wstring toUTF16( in char[] s ); 
const( wchar )* toUTF16z( in char[] s ); 
wstring toUTF16( in wchar[] s ); 
wstring toUTF16( in dchar[] s );
dstring toUTF32( in char[] s ); 
dstring toUTF32( in wchar[] s ); 
dstring toUTF32( in dchar[] s );
import std.stdio, std.utf;

  /*****************************
  *   wcharの文字コード表示
  */
void dispCode( in wchar[] code ){
  write( "[ " );
  foreach( i, c; code ){
    if( i ) write( ", " );
    writef( "0x%04x", cast( uint )c );
  }
  writeln( " ]" );
}

  /*****************************
  *
  *
  */
void main(){
  string s = "文字コードの変換";
  dispCode( toUTF16( s ) );
  
  // ソースコードはUTF-8
  wstring ws = "文字コードの変換";
  dispCode( ws );
  dispCode( "文字コードの変換" );
  
  // これじゃ無理かな?
  writeln();
  dispCode( cast( wstring )s );
}
D:\Pen-Jr\My Programs\supp>test
[ 0x6587, 0x5b57, 0x30b3, 0x30fc, 0x30c9, 0x306e, 0x5909, 0x63db ]
[ 0x6587, 0x5b57, 0x30b3, 0x30fc, 0x30c9, 0x306e, 0x5909, 0x63db ]
[ 0x6587, 0x5b57, 0x30b3, 0x30fc, 0x30c9, 0x306e, 0x5909, 0x63db ]

[ 0x96e6, 0xe587, 0x97ad, 0x82e3, 0xe3b3, 0xbc83, 0x83e3, 0xe389, 0xae81, 0xa4e5
, 0xe689, 0x9b8f ]
文字数
size_t toUCSindex( in char[] s, size_t i ); 
size_t toUCSindex( in wchar[] s, size_t i ); 
size_t toUCSindex( in dchar[] s, size_t i );
size_t count( E )( const( E )[] s );
Phobos( 標準ライブラリ
正規表現
式( Expressions
論理演算
代入式
型( Types
配列
ポインタ
別名宣言( alias
構造体・共用体( struct & union
連想配列( Associative Arrays
クラス( class
属性( Attributes
記憶クラス
制御文( Statements
関数
E-Mail : open@pen-jr.org

ご意見やご指摘は大歓迎ですが、質問など返信は期待しないでください

pen jr.
D言語研究
わかったつもりになるD言語
D言語友の会
News&Days
はじめてのブログ選び