文字列の注意点(その2)

文字列をファイルに書き込むとVB5の場合、コード変換が行われます。
これはVB特有の動作で、将来に向けての内部はユニコードに、互換性を考えてファイルはDBCSに変換されます。
しかし、初心者には分かりにくい仕様であり、テキストファイルの読み書きが遅くなるという問題点を持っています。

C++Builder1.x と Delphi3.x はDBCSを標準にしており、現時点でメモリ内部とファイルは同じコードで読み書きします。


VBの場合

ファイルに書き込んだ"ABCあいう" のデータ
バイト数 1 2 3 4 5 6 7 8 9
1バイト毎のデータの並び
(16進)
41 42 43 82 A0 82 A2 82 A4
文字数 1 2 3 4 5 6
実際の文字 "A" "B" "C" "あ" "い" "う"

確認に使用したコード


Private Sub Command1_Click()
    Dim length As Integer
    Dim cnt As Integer
    Dim char1 As String
    Dim ascii As Integer
    Dim swork As String
    Dim rwork(0 To 12) As Byte  ' ユニコード分のサイズを用意しておく
    Dim work As String
    Dim msg As String
    
    '* Stringの内容をチェック
    swork = "ABCあいう"
    '* バイナリイメージで書き込む
    Open "STRINGCHECK.TXT" For Binary Access Write As #1
    Put #1, , swork
    Close #1
    
    msg = "元の文字列=" + swork + Chr(13) + Chr(10)
    length = LenB(swork)
    For cnt = 1 To length
        ' 1バイト取り出して、文字コードに変換する
        char1 = MidB(swork, cnt, 1)
        ascii = AscB(char1)
        ' 文字コードを16進数の文字列に変換する
        msg = msg + Right("00" + Hex(ascii), 2) + ","
    Next cnt
    msg = msg + Chr(13) + Chr(10)
    
    '* バイナリイメージをコード変換せずに読み込む
    Open "STRINGCHECK.TXT" For Binary Access Read As #1
    Get #1, , rwork
    Close #1

    '* バイナリイメージをコード変換して読み込む
    Open "STRINGCHECK.TXT" For Binary Access Read As #1
    Input #1, work
    Close #1
    
    msg = msg + "読み込んだ文字列=" + work + Chr(13) + Chr(10)
    For cnt = LBound(rwork) To UBound(rwork)
        ' 1バイト取り出して、文字コードに変換する
        ascii = rwork(cnt)
        ' 文字コードを16進数の文字列に変換する
        msg = msg + Right("00" + Hex(ascii), 2) + ","
    Next cnt
    Call MsgBox(msg)
    
End Sub


C++Builderの場合

ファイルに書き込んだ"ABCあいう" のデータ
バイト数 1 2 3 4 5 6 7 8 9
1バイト毎のデータの並び
(16進)
41 42 43 82 A0 82 A2 82 A4
文字数 1 2 3 4 5 6
実際の文字 "A" "B" "C" "あ" "い" "う"

確認に使用したコード


//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  int  cnt;
  int  ascii;
  char  rwork[128];

  // AnsiStringの内容をチェック
  AnsiString swork = "ABCあいう";
  AnsiString msg = "元の文字列=" + swork + "\xd\xa";
  int len = swork.Length() + 1;
  for( cnt = 1; cnt <= len; cnt ++ ){
    // 1バイト取り出して、文字コードに変換する
    ascii = int(swork[cnt]) & 0xff;
    // 文字コードを16進数の文字列に変換する
    msg += IntToHex(ascii, 2) + ",";
  }
  msg += "\xd\xa";

  // 書き込み
  TFileStream *fs;
  fs = new TFileStream( "STRINGCHECK.TXT", fmCreate );
  fs->Write( (void *)&swork[1], len);  // 要注意:AnsiStringは[1]が先頭
  delete fs;

  // 読み込み
  memset( rwork, 0, sizeof(rwork) );
  fs = new TFileStream( "STRINGCHECK.TXT", fmOpenRead );
  fs->Read( rwork, len);
  delete fs;

  msg = msg + "読み込んだ文字列=" + rwork + "\xd\xa";
  len = strlen(rwork) + 1;
  for( cnt = 1; cnt <= len; cnt ++ ){
    // 1バイト取り出して、文字コードに変換する
    ascii = int(swork[cnt]) & 0xff;
    // 文字コードを16進数の文字列に変換する
    msg += IntToHex(ascii, 2) + ",";
  }
  ShowMessage(msg);

}
//--------------------------------------------------------------------------- 


Delphiの場合

ファイルに書き込んだ"ABCあいう" のデータ
バイト数 1 2 3 4 5 6 7 8 9
1バイト毎のデータの並び
(16進)
41 42 43 82 A0 82 A2 82 A4
文字数 1 2 3 4 5 6
実際の文字 "A" "B" "C" "あ" "い" "う"

確認に使用したコード


procedure TForm1.Button1Click(Sender: TObject);
var
  len: Integer;
  cnt: Integer;
  ascii: Integer;
  swork: AnsiString;
  rwork: AnsiString;
  msg: AnsiString;
  F1: TextFile;
  Ch: Char;
begin
  // AnsiStringのイメージをそのままファイルに書き込む
  swork := 'ABCあいう';
  msg := '元の文字列=' + swork + #13#10;
  len := Length( swork );
  for cnt := 1 to len do
  begin
    // 1バイト取り出して、文字コードに変換する
    ascii := Ord( swork[cnt] );
    // 文字コードを16進数の文字列に変換する
    msg := msg + IntToHex( ascii, 2) + ',';
  end;
  msg := msg + #13#10;

  // 書き込み
  AssignFile( F1, 'STRINGCHECK1.TXT' );
  Rewrite( F1 );
  Write( F1, swork );
  CloseFile( F1 );

  // 読み込み
  AssignFile( F1, 'STRINGCHECK1.TXT' );
  Reset( F1 );
  while not Eof( F1 ) do  // ファイルの終端までループ
  begin
    Read( F1, Ch );
    rwork := rwork + Ch;
  end;
  msg := msg + '読み込んだ文字列=' + rwork + #13#10;

  Reset( F1 );
  while not Eof( F1 ) do  // ファイルの終端までループ
  begin
    Read( F1, Ch );
    msg := msg + IntToHex( Ord( Ch ), 2) + ',';
  end;
  CloseFile( F1 );

  ShowMessage( msg );

end;

 

WideString 型の使用例です。無理矢理書き込んで、無理矢理読み込んでいます(^^;

ファイルに書き込んだ"ABCあいう" のデータ
バイト数 1 2 3 4 5 6 7 8 9 10 11 12
1バイト毎のデータの並び
(16進)
41 00 42 00 43 00 42 30 44 30 46 30
文字数 1 2 3 4 5 6
実際の文字 "A" "B" "C" "あ" "い" "う"

確認に使用したコード


procedure TForm1.Button2Click(Sender: TObject);
var
  len: Integer;
  cnt: Integer;
  ascii: Integer;
  swork: WideString; // ワイド文字対応
  rwork: WideString; // ワイド文字対応
  wCh: WideChar;
  size: Integer;
  msg: AnsiString;
  S: array[0..256] of Char;
  fs: TFileStream;
  F1: TextFile;
  Ch: Char;
begin
  // WideStringのイメージをそのままファイルに書き込む
  swork := 'ABCあいう';
  msg := '元の文字列=' + swork + #13#10;
  len := Length( swork );
  for cnt := 1 to len do
  begin
    StrLCopy( S, PChar( @swork[cnt] ), 2 );
    // 1バイトづつ取り出して、文字コードに変換する
    ascii := Ord( S[0] );
    msg := msg + IntToHex( ascii, 2) + ',';
    ascii := Ord( S[1] );
    msg := msg + IntToHex( ascii, 2) + ',';
  end;
  msg := msg + #13#10;

  // 書き込み
  fs := TFileStream.Create( 'STRINGCHECK2.TXT', fmCreate );
  for cnt := 1 to len do  // 文字数ループ
  begin
    wCh := swork[cnt];
    fs.Write( wCh, 2 );
  end;
  fs.Free;

  // 読み込み
  rwork := '';
  fs := TFileStream.Create( 'STRINGCHECK2.TXT', fmOpenRead );
  size := fs.Read( wCh, 2 );
  while size <> 0 do  // 読み込みサイズが0になるまでループ
  begin
    rwork := rwork + WideString(wCh);
    size := fs.Read( wCh, 2 );
  end;
  fs.Free;
  msg := msg + '読み込んだ文字列=' + rwork + #13#10;

  AssignFile( F1, 'STRINGCHECK2.TXT' );
  Reset( F1 );
  while not Eof( F1 ) do  // ファイルの終端までループ
  begin
    Read( F1, Ch );
    msg := msg + IntToHex( Ord( Ch ), 2) + ',';
  end;
  CloseFile( F1 );

  ShowMessage( msg );

end;


戻る