Delphi 2009とUnicode:Part II
この記事は以前EDNサイトに作成されていた記事を転載したものです。
目次
概要
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;
環境によっては、すべての文字列を表示できないかもしれませんので、コードとこれをダイアログに表示させた画面ショットを紹介しておきましょう。
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を使うかです。