Delphi 2009 とUnicode : Part II

投稿者:: Hideaki Tominaga

概要: Delphi 2009 でのUnicode の扱い方と基本的な Unicode の構造について解説します。

“Delphi 2009 と Unicode” 第二回目は、Delphi 2009 で Unicodeを使ったコーディングを行う方法を説明します。

    Unicode と 文字列型

以前の Delphi では、String 型は AnsiString 型でした。これは 8bit の ANSI 文字列を格納するための型でした。Delphi 2009 では、String 型は UnicodeString 型となっており、16bit の Unicode 文字列(UTF-16 / UCS2) を格納する型になっています。

素朴な疑問としては、「従来の WideString型 とは何が違うの?」と言う事でしょう。簡単に言えば、参照カウントがあるのが UnicodeString で、ないのが WideString です。これについては以降で説明する事があるかもしれませんが、今は「そういうものだ」と理解しておいて下さい。

    初めての Unicode アプリケーション

実際、Delphi 2009で Unicode アプリケーションを作るのは簡単です。

とりあえず、新規にプロジェクトを作成してボタンを貼り付け、クリック時のイベントに以下のようなコードを記述してみます。

procedure TForm1. Button1Click(Sender: TObject);
var
  S: String;
begin
  S := 'Hello, World.';
  ShowMessage(S);
end;

なんだか、とても Unicode を扱っているようには思えませんね。でも、Delphi 1から変わらないこのコードは、Delphi 2009で、内部的に大きく変わっています。Delphi 2009では、Unicodeを標準文字列型として採用しているために、このコードで使われている文字列はすべてUnicodeです。つまり、

  • 文字列 S は、UnicodeStringです。
  • Sに代入している ‘Hello, World.’ は、Unicodeです。
  • ShowMessageの内部では、UnicodeStringとして表示する文字列を処理します。

これまで、AnsiStringの世界で行われてきた文字列の受け渡しは、すべてUnicodeになったのです。

VCLアプリケーションの場合、コントロールを介した文字列の入力、あるいは表示では、Unicodeが出入り口の文字コードとして機能しています。つまり、入力された文字列はUnicodeとして処理し、Unicodeとしてコントロールに渡して出力します。Delphi 2009では、これが自然に行われているというわけです。

    もう少しUnicode らしい例

Unicodeアプリケーションを作っているという実感が湧きませんか?では、次のコードを。

  S := S+ '';
  S := S + 'こんにちは' + #$000D#$000A;
  S := S + 'Hello' + #$000D#$000A;
  S := S + '你好' + #$000D#$000A;
  S := S + '안녕하세요' + #$000D#$000A;
  S := S + 'Здравствый!' + #$000D#$000A;
  S := S + 'Xin chào' + #$000D#$000A;

環境によっては、すべての文字列を表示できないかもしれませんので、コードとこれをダイアログに表示させた画面ショットを紹介しておきましょう。

Hide image
sample

図 1 Unicodeによる多言語表示の例

Unicode を使う最大のメリットは、このように世界各国の言葉を同時に表示可能な事です。

ANSI と一口に言っても、コードページが異なれば 0x80-0xff に割り当てられる文字も異なるため、例えばフランス語とロシア語を同時に表示させる事は無理でした。Unicode アプリケーションであれば、多言語化を比較的簡単にこなす事ができます。

もう一つ例を挙げてみましょう。

  S := S+ '';
  S := S + '丈' + #$000D#$000A; // #$4E08
  S := S + #$2000B + #$000D#$000A;
  S := S + '©' + #$000D#$000A; // #$00A9
  S := S + '®' + #$000D#$000A; // #$00AE

多言語化やグロバリゼーション以外でも、Unicode 化のメリットを享受できる例です。コードポイントU+2000B の文字は、’ 丈’ (U+4E08) の異体字です。この文字はサロゲートペアとなります。

Unicode では マルチバイト ANSI に比べて利用できる文字の数が増えており、Shift-JIS では表現できなかった’ ©’ や ‘®’ 、外字にするしかなかった文字等も表示する事ができます。このように、旧来の Delphi でマルチバイト ANSI を扱ってきた方にも、アプリケーションをUnicode 化するメリットはちゃんとあります。

サロゲートペアについては、ここでは詳細に説明しませんが、別の機会があれば、あらためて触れたいと思います。Delphi 2009でUnicodeを扱う例として認識して頂ければいいでしょう。

    文字エンコーディング変換

Delphi 2009 でのコーディングでは、文字列をString(UnicodeString) 型で処理するのが一般的です。が、時としてShift-JIS 等を扱うため、または過去の資産を活かすために AnsiString 型を使う事や、Web アプリケーションのために UTF-8 を使いたい事もあるでしょう。

文字エンコーディング変換の一例を以下に示してみます。

Var
  u8: UTF8String;
  U: UnicodeString;
  A: AnsiString;
Begin
  A := 'こんにちは';
  U := A;
  U8 := U;
  ShowMessage(A);
  ShowMessage(U);
  ShowMessage(U8);
End;

とっても、お手軽ですね。例にあるように Delphi 2009 の代入は非常に強力で、殆どの文字エンコーディング変換を代入で済ます事ができます。

もちろん、旧来の Delphi に存在した文字エンコーディング変換関数も引き続き利用できます。

  • AnsiToUTF8 / UTF8ToAnsi
    ANSI <-> UTF-8 を変換します。
  • UTF8Encode / UTF8Decode
    UTF-8 <-> UTF-16 を変換します。Delphi 2007 のものとは違い、4バイト文字(サロゲートペア)を正しく処理します。
  • WideStringToUCS4String / UCS4StringToWideString
    UTF-16 <-> UTF-32 を変換します。Delphi 2007 のものとは違い、サロゲートペアを正しく処理します。

先に述べた通り、Delphi 2009 に於いては代入で殆どの文字エンコーディング変換が可能なため、これらの変換関数を使う場面というのは非常に限られてくるのですが、既存のプロジェクトのマイグレーション(移行)には欠かせないものとなっています。

他にも多くの文字エンコーディング変換関数が用意されています。詳しくは System 名前空間を参照してみて下さい。

    ファイル処理

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

procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.Add('Tiburón');
    // File Save
    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('Tiburón');
    // File Save
    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('Tiburón');
    // File Save
    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('Tiburón');
    // File Save
    SL.SaveToFile('C:\Ansi.txt', Enc);
  finally
    Enc.Free;
    SL.Free;
  end;
end;

GetEncodingを使えば、"任意のANSIコードページ" でファイルの入出力が可能となります。

また、TEncoding.UTF8 ではなく、GetEncoding(65001) を利用すれば、BOM 無し UTF-8(UTF-8N) を入出力する事が可能です。

但し、GetEncoding()を利用する場合には、サンプルコードのように Free を使ってオブジェクトを破棄する必要があります

    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', 'Tiburón');
  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', 'Tiburón');
  finally
    Ini.Free;
  end;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create('C:\Test.ini', TEncoding.Unicode);
  try
    Ini.WriteString('TEST', 'Item01', 'Tiburón');
    Ini.UpdateFile;
  finally
    Ini.Free;
  end;
end;

TMemIniFileでTEncodingを使うかです。

この記事は、自作のDelphi Tips「Unicode」を元にCDN用に編集・加筆したものです。オリジナルのTips集は、こちらのページをご覧ください。


関連記事:

次からのサーバー応答:: ETNASC03