Delphi 2009 以降とファイル処理

LoadFromFile/SaveToFile
 TStringListクラスでLoadFromFile/SaveToFileするのが最も簡単にファイル入出力をする方法です。

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.Add('あいうえお');
    // ファイル保存
    SL.SaveToFile('C:\Default.txt');
  finally
    SL.Free;
  end;
end;

 このようなコードでUnicode(UTF-16)のファイルが簡単に...吐けません。このコードで吐かれるのは日本語環境ならデフォルトコードページのCP932(Shift-JIS)になります。 Unicode(UTF-16)でファイルを吐きたいのなら、以下のようにTEncodingクラスを指定する必要があります。

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.Add('あいうえお');
    // ファイル保存
    SL.SaveToFile('C:\Unicode.txt'TEncoding.Unicode);
  finally
    SL.Free;
  end;
end;

 面倒...たしかにそうですね。ですが、以下のような事もできます。

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.Add('あいうえお');
    // ファイル保存
    SL.SaveToFile('C:\UTF-16BE.txt', TEncoding.BigEndianUnicode); // UTF-16BE
    SL.SaveToFile('C:\UTF-8.txt'   , TEncoding.UTF8);               // UTF-8
    SL.SaveToFile('C:\UTF-7.txt'   , TEncoding.UTF7);               // UTF-7
  finally
    SL.Free;
  end;
end;

 さらに、

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
  Enc: TEncoding;
begin
  SL := TStringList.Create;
  Enc := TEncoding.GetEncoding(20932); // EUC-JP(CP20932)
  try
    SL.Add('あいうえお');
    // ファイル保存
    SL.SaveToFile('C:\Ansi.txt', Enc);
  finally
    Enc.Free;
    SL.Free;
  end;
end;

 GetEncodingを使えば、"任意のANSIコードページ"でファイルの入出力が可能となります。GetEncoding()を利用する場合には、Free を使ってインスタンスを破棄する必要があります。TEncodingにUTF-32LE/BEが用意されていないのが少々残念です。しかしながら、totonica氏作の TUCS4Encoding を使えば、TEncodingを使う要領で UTF-32LE/BE を利用する事が可能となります。

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
  Enc: TEncoding;
begin
  SL := TStringList.Create;
  Enc := TEncoding.GetEncoding('euc-jp'); // EUC-JP(CP20932)
  try
    SL.Add('あいうえお');
    // ファイル保存
    SL.SaveToFile('C:\Ansi.txt', Enc);
  finally
    Enc.Free;
    SL.Free;
  end;
end;

 XE2 以降ではエンコーディング名を文字列で指定可能になりました。指定できるコードページ文字列は Code Page Identifiers (MSDN) で確認する事ができます。

IniFile
 IniFileも、Unicode(UTF-16)に対応しています。

procedure TForm1.Button1Click(Sender: TObject);
var
  Ini: TIniFile;
begin
  Ini := TIniFile.Create('C:\Test.ini');
  try
    Ini.WriteString('TEST''Item01''あいうえお');
  finally
    Ini.Free;
  end;
end;

 ですが、Iniファイルが存在しない場合に上記のコードで吐かれるのは日本語環境ならデフォルトコードページのCP932(Shift-JIS)となります。常にUnicode(UTF-16)でファイルを吐くには、

procedure TForm1.Button1Click(Sender: TObject);
var
  Ini: TIniFile;
  SL: TStringList;
  FileName: String;
begin
  FileName := 'C:\Test.ini';
  // 空のUnicode(UTF-16)ファイルを作成してから
  if not FileExists(FileName) then
    begin
      SL := TStringList.Create;
      try
        SL.SaveToFile(FileName, TEncoding.Unicode);
      finally
        SL.Free;
      end;
    end;
  // Iniファイルを操作する
  Ini := TIniFile.Create(FileName);
  try
    Ini.WriteString('TEST''Item01''あいうえお');
  finally
    Ini.Free;
  end;
end;

 このようにファイルチェックを行い、ファイルが存在しなければ事前に空のUnicode(UTF-16)ファイルを生成してやる必要があります。或いは...

procedure TForm1.Button1Click(Sender: TObject);
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create('C:\Test.ini'TEncoding.Unicode);
  try
    Ini.WriteString('TEST''Item01''あいうえお');
    Ini.UpdateFile;
  finally
    Ini.Free;
  end;
end;

 TMemIniFileTEncodingを使うかです。


Delphi 2009 以降と文字列処理 (2)

TStringBuilderクラス
 従来の手続き型文字列処理に慣れ親しんだヒトには好き嫌いが分かれると思いますが、Delphi2009にはもう一つの文字列処理系があります。それはTStringBuilderクラスを用いた文字列処理です。

procedure TForm1.Button1Click(Sender: TObject);
var
  SB: TStringBuilder;
begin
  SB := TStringBuilder.Create;
  try
    SB.Append('あいう');
    SB.Append(1000);
    SB.Append(True);
    SB.AppendFormat('%s %.4d',['TEST'1]);
    ShowMessage(SB.ToString);
  finally
    SB.Free;
  end;
end;

 TStringBuilderクラスは.NETのStringBuilderクラスと互換性を持ちますが、AppendFormatで使える書式文字列は".NETの複合書式指定文字列"ではなく、従来よりDelphiのFormat()で使われていた書式文字列になります。

 Appendメソッドが数多くオーバーロードされているので、いちいち型変換関数を噛ます必要がないのは利点ですが、Insertメソッドがワードインデックス(サロゲートペアは2ワード扱い)での処理だったりと、本格的に文字列処理を行いたいのであればどうしても他のRTL関数を利用する必要があり、.NETの開発者がDelphi2009で開発を行う際の支援、或いはWin32と.NETで限りなく等価なコードを記述する場合以外には使いどころが難しいクラスだと思います。 どちらかと言えばC++Builder2009での方が利用価値は高いのではないかと思われます。

警告の昇格
 "Delphi2009と文字列処理 (1)"で述べた通り、AnsiStringでの文字列操作及び、AnsiStrings名前空間を扱う場合には細心の注意が必要となります。 AnsiString<->UnicodeStringへの代入は言うまでもない事ですが、誤ってAnsiStringでUnicode版関数を使ったり、またはその逆を行うと予想しない結果になる事があります。

 "AnsiStringでUnicode版関数を使った時の警告"と、"UnicodeでAnsiString版関数を使った時"の警告が非常に似ているので、慣れてくると「ああ、またか。これはOKだよ」と警告を見過ごしてしまいがちになります(経験談)。 繰り返しますが代入互換性の問題はトラブルの元なので、もういっその事、警告からエラーに昇格させてしまいましょう。

uses
  ...;

// "暗黙的文字列キャスト(W1057)"をエラーに昇格
{$WARN IMPLICIT_STRING_CAST ERROR}

// "暗黙的文字列キャストによるデータ喪失の可能性(W1058)"をエラーに昇格
{$WARN IMPLICIT_STRING_CAST_LOSS ERROR}

function Test(aPath: AnsiString): AnsiString;
begin
  result := ExcludeTrailingPathDelimiter(aPath);
end;

 上記コードはAnsiStringsをuses句に追加しないとエラーで止まるようになります。下記コードはCharInSet()を利用しないとエラーで止まるようになります。

// "set 式で WideChar がバイト文字に変換されました(W1050)"をエラーに昇格
{$WARN WIDECHAR_REDUCED ERROR}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  S: String;
begin
  S := '12A';
  for i:=1 to Length(S) do
    if (S[1in ['A'..'Z']) then
      ShowMessage('Hit!');
end;

 コンパイラ指令 {$WARN} の書式は以下のようになっています。

{$WARN identifer ON | OFF | ERROR | DEFAULT}

 ON/OFFでワーニング有効/無効になり、ERRORを指定すると警告からエラーへ昇格します。DEFAULTを指定すると既定の動作に戻ります。"identifer"に指定できる識別子の一覧を以下に掲示しておきます。

Identifer: Error No: Description:
SYMBOL_DEPRECATED W1000 シンボル '%s' を使用することは推奨されていません
SYMBOL_LIBRARY W1001 シンボル '%s' はライブラリに依存すると宣言されています
SYMBOL_PLATFORM W1002 シンボル '%s' はプラットフォームに依存すると宣言されています
SYMBOL_EXPERIMENTAL W1003 シンボル '%s' はテスト用です
UNIT_LIBRARY W1004 ユニット '%s' はライブラリに依存すると宣言されています
UNIT_PLATFORM W1005 ユニット '%s' はプラットフォームに依存すると宣言されています
UNIT_DEPRECATED W1006 ユニット '%s' を使用することは推奨されていません
UNIT_EXPERIMENTAL W1007 ユニット '%s' はテスト用です
HRESULT_COMPAT x1008 Integer と HRESULT が入れ替わっています
HIDING_MEMBER W1009 '%s' の再宣言のため基本クラスのメンバーが隠蔽されました
HIDDEN_VIRTUAL W1010 '%s' メソッドが基本型 '%s' の仮想メソッドを隠しました
GARBAGE W1011 コンパイラは 'end.' 以降の文字を無視します
BOUNDS_ERROR x1012 範囲外の定数式です
ZERO_NIL_COMPAT W1013 定数 0 が NIL に変換されました
STRING_CONST_TRUNCED W1014 string[%ld] に収まるように文字列定数が丸められました
FOR_LOOP_VAR_VARPAR W1015 for 文の制御変数 '%s' を変数パラメータとして渡すことはできません
TYPED_CONST_VARPAR W1016 型付き定数 '%s' が変数パラメータに渡されています
ASG_TO_TYPED_CONST W1017 型付き定数 '%s' への代入
CASE_LABEL_RANGE W1018 case 式の範囲外のラベルがあります
FOR_VARIABLE x1019 for 文の制御変数はローカル変数でなければなりません
CONSTRUCTING_ABSTRACT x1020 クラス '%s' のインスタンスを作成していますが,このクラスは抽象メソッド '%s.%s' を含みます
COMPARISON_FALSE W1021 比較結果は常に False に評価されます
COMPARISON_TRUE W1022 比較結果は常に True に評価されます
COMPARING_SIGNED_UNSIGNED W1023 符号付型と符号なし型の比較のため,オペランドが拡張されました
COMBINING_SIGNED_UNSIGNED W1024 符号付型と符号なし型の演算により,オペランドが拡張されました
UNSUPPORTED_CONSTRUCT x1025 この仕様はサポートされていません:'%s'
FILE_OPEN x1026 ファイルが見つかりません:'%s'
FILE_OPEN_UNITSRC F1027 '%s' ユニットまたは対応するバイナリ (%s) が見つかりません
BAD_GLOBAL_SYMBOL x1028 不正なグローバルシンボル '%s' がオブジェクトファイル '%s' にあります
DUPLICATE_CTOR_DTOR W1029 パラメータが等しい複数の %s は C++ から参照できません : %s
INVALID_DIRECTIVE x1030 コンパイラ指令に間違いがあります:'%s'
PACKAGE_NO_LINK W1031 -J オプションが指定されているため,パッケージ '%s' をディスクに書き出すことはできません
PACKAGED_THREADVAR W1032 スレッドローカル変数 '%s.%s' がエクスポートされていますが,パッケージの外からこの変数を使用することはできません
IMPLICIT_IMPORT x1033 ユニット '%s' は暗黙のうちにパッケージ '%s' に組み込まれました
HPPEMIT_IGNORED W1034 $HPPEMIT '%s' は無視されました
NO_RETVAL W1035 関数 '%s' の戻り値が設定されていません
USE_BEFORE_DEF W1036 変数 '%s' は初期化されていない可能性があります
FOR_LOOP_VAR_UNDEF W1037 for ループ脱出後のループ制御変数 '%s' の値は不定です
UNIT_NAME_MISMATCH E1038 Unit で指定された '%s' がファイル名と一致しません
NO_CFG_FILE_FOUND W1039 環境設定ファイルが見つかりません
IMPLICIT_VARIANTS W1040 Variants ユニットを使用します
UNICODE_TO_LOCALE W1041 Unicode 文字からロケール文字セットへの変換エラー。文字列は切り詰められます。LANG 環境変数を確認してください
LOCALE_TO_UNICODE W1042 ロケール文字列 '%s' から Unicode への変換エラー。文字列は切り詰められます。LANG 環境変数を確認してください
IMAGEBASE_MULTIPLE W1043 Imagebase $%X は 64k の倍数ではありません。$%X に丸めてください
SUSPICIOUS_TYPECAST W1044 %s から %s への型キャストは間違っている可能性があります
PRIVATE_PROPACCESSOR W1045 親クラスでは private 宣言されている '%s.%s' を参照したプロパティが定義されました
UNSAFE_TYPE W1046 安全でない型 '%s%s%s'
UNSAFE_CODE W1047 安全でないコード '%s'
UNSAFE_CAST W1048 '%s' から '%s' への安全でないタイプキャスト
OPTION_TRUNCATED W1049 値 '%s' (オプション %s) が切り捨てられました
WIDECHAR_REDUCED W1050 設定式で WideChar がバイト文字に変換されました
DUPLICATES_IGNORED W1051 名前空間でシンボル名が重複しています。'%s.%s' (%s) を使用します。%s の重複は無視します
UNIT_INIT_SEQ W1052 System.Runtime.CompilerServices.RunClassConstructor が見つかりません。ユニットの初期化順序は uses 節の順序にしたがいません
LOCAL_PINVOKE W1053 ローカル PInvoke コードは,外部ルーチン '%s'(パッケージ '%s' 内)がそのカスタム属性でパッケージローカル型を使って定義されているので作成されませんでした
MESSAGE_DIRECTIVE x1054 リンカエラー : %s
TYPEINFO_IMPLICITLY_ADDED W1055 Published によって型に RTTI ($M+) が追加されます '%s'
RLINK_WARNING x1056 %s%s%s (RLINKの警告)
IMPLICIT_STRING_CAST W1057 文字列の暗黙的なキャスト ('%s' から '%s')
IMPLICIT_STRING_CAST_LOSS W1058 データ損失の可能性がある文字列の暗黙的なキャスト ('%s' から '%s')
EXPLICIT_STRING_CAST W1059 文字列の明示的なキャスト ('%s' から '%s')
EXPLICIT_STRING_CAST_LOSS W1060 データ損失の可能性がある文字列の明示的なキャスト ('%s' から '%s')
CVT_WCHAR_TO_ACHAR x1061 与えられた WideChar 定数 (#$%04X) を AnsiChar に縮小変換した結果、情報が失われました
CVT_NARROWING_STRING_LOST x1062 与えられたワイド文字列定数を縮小変換した結果、情報が失われました
CVT_ACHAR_TO_WCHAR x1063 与えられた AnsiChar 定数 (#$%02X) を WideChar に拡大変換した結果、情報が失われました
CVT_WIDENING_STRING_LOST x1064 与えられた AnsiString 定数を拡大変換した結果、情報が失われました

 エラーコードの先頭が"x"のものは状況によって"E(Error)"だったり"F(Fatal)"だったり"W(Warning)"だったりと不定です。{$WARN identifer ERROR} で昇格後は"Exxxx"とエラー表示されます。


 BACK