Delphi 2009とUnicode:Part III
この記事は以前EDNサイトに作成されていた記事を転載したものです。
目次
概要
Delphi 2009でのUnicodeの扱い方と基本的なUnicodeの構造について解説します。
「Delphi 2009とUnicode」第三回目は、Delphi 2009とUnicodeのトラブルシューティングについてです。
Unicode を表示できない!
いかにDelphi 2009がUnicodeに対応していても、作成したアプリケーションを配布する先の PCにUnicodeのフォントがインストールされていなくては話になりません。Windowsが標準で持っているフォントで表示可能な文字が一番多いのは、「MingLiU」または「SimSun」だと思われます。メイリオ(Meiryo)は、あくまでも「JIS X 0213:2004」の範囲の文字しかサポートしていません。但し、メイリオ(Meiryo)にも、「MingLiU」または「SimSun」に存在しない文字が格納されています。
Office 2000以降をお持ちであれば、製品に付属している「Arial Unicode MS」をインストールして使うこともできます。このフォントはプロポーショナルフォントのみですが、Unicode 2.1規格のすべての文字を網羅しています。
Windows(Vista)と「JIS X 0213:2004」に関する資料は「Windows Vista ならびに Windows Server 2008 における JIS2004 対応に関する詳細資料」としてまとめられていますので、御一読下さい。
サロゲートペアに関する問題
サロゲートペアを表示するための設定
UCS2/BMPの文字は普通のUnicodeフォントで表示されますが、サロゲートペアともなると話は変わってきます。
- Windows 2000の場合。
- レジストリを変更しないとサロゲートペアが扱えません。具体的には、[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\LanguagePack]の「SURROGATE」を0x00000002(REG_DWORD)に設定します。
- さらに、フォントがありません。Microsoft のサイトから「Windows 2000 Surrogate 更新」をダウンロードしてインストールする必要があります。(注:フォントのダウンロードは現在提供されていません)
- Windows XP/Server 2003 の場合
- 東アジア版以外のWindowsでは、Windows 2000同様にレジストリの変更が必要となります。東アジア版 には「MingLiU」や「SimSun」といったUnicode対応フォントがインストールされているはずです。綺麗なフォントを使いたければ、「JIS X 0213:2004」の範囲しかサポートされませんが、日本語フォントのメイリオ(Meiryo)をMicrosoftのサイトからダウンロードして下さい(「Windows XP向けClearType対応日本語フォント」)。(注:フォントのダウンロードは現在提供されていません)
- Windows Vista の場合
- 特にありません。
IDEにおけるサロゲートペアの表示
XP環境下のDelphi 2009IDE では、サロゲートペアを表示できません。これは、IDEが利用するシステムフォント「Tahoma」に、サロゲートペアを表示可能なフォントがリンクされていないためです。
「フォントリンク」とは、あるフォントに存在しない文字を補うために、別のフォントをリンクする機構です。欧文フォントで日本語が表示できるのはこのおかげですが、あくまで別の書体のフォントをリンクしているため、場合によってはリンクされた文字だけが太字で表示されたり、一回り小さく表示される事があります。
Vista環境下では、オブジェクトインスペクタを初め、IDEのあらゆる箇所でサロゲートペアの入力/表示が可能です。IDEのコードエディタで一部のサロゲートペアが表示されない場合には、コードエディタに利用しているフォントを変更するといいでしょう。「Courier New」や、「MingLiU」を指定してみて下さい。
XP環境下では、一部のVCL …例えばTRibbonGroupやTCategoryPanelGroupのキャプション部分などでサロゲートペアが表示されない事があります。これもIDEの問題と同様に、フォントとして「Tahoma」が利用されている事に起因しています。
フォントリンクの設定を変更することはできますが、レジストリの操作を伴う上、他のアプリケーションに影響を及ぼす可能性があるため、一般的な解決方法にはなりえないでしょう。
文字単位で文字列操作できない!
String/UnicodeString/WideString (UTF-16)
UTF-16で「サロゲートペア」と呼ばれる文字は、Unicodeでは特殊な文字ではありません。マルチバイトにおける2バイト文字のようなものです。
旧来のDelphiでは、「文字単位で文字列を操作する」場合には、マルチバイト文字を考慮してくれる「Ansi~」という名前で始まる関数を利用しました。しかしながら、Delphi 2009 の「Ansi~」と付いたUnicode版関数は、引数がAnsiStringからUnicodeStringになるので、「Ansi~」と付いたUnicode版関数でサロゲートペアを処理できる...訳ではありません。
ByteType()のAnsiString版はマルチバイトを、UnicodeString版はマルチワードを判定するので、「Ansi~と付いたUnicode版関数はサロゲートペアを考慮するのでは?」と思いがちですが、残念ながら現状ではそうなっていません。
誤解を招きそうなので補足しておきますが、Delphi 2009にはサロゲートペアを考慮した RTLがちゃんと用意されています。ただ、「Copy() の文字単位版」のようなお手軽な関数は用意されていない、という事です。
サロゲートペアに関しては、それを考慮した文字列操作の方法も含めて、別の記事で触れたいと思います。
UTF8String (UTF-8)
UTF8Stringを「Ansi~」と名の付く関数に渡しても文字列操作がうまく行えない事があります。これはUTF-8は最大4(6)バイトのマルチバイト文字なのですが、WindowsのAPIも含めて「マルチバイトは最大で2バイト」という前提の処理があるためです。
UTF8Stringでの「文字単位での文字列操作」は煩雑になりがちなので、String型(UTF-16)で文字列操作を行ってから、UTF-8へコンバートを行うといいでしょう。
その他のコーディング上の問題・注意点
In 演算子で警告になる!
旧来のDelphiでは以下のようなコードが普通に使われていました。
var S: set of Char; begin S := ['A'..'Z']; if ('A' in S) then ... end;
しかし、Delphi 2009ではこのコードは通りません。これを回避するには CharInSet()を使います。
var S: set of Char; begin S := ['A'..'Z']; if CharInSet('A', S) then ... end;
CharInSet()はString(UnicodeString)にもAnsiStringにも使えます。
関数の名前と挙動が一致しない!
プロジェクト移行を容易にするため、旧来AnsiString用だった関数の殆どにUnicodeString版が用意してあります。しかし、引数をAnsiStringからUnicodeStringにしてしまうと名前と挙動が一致しなくなるものがあります。
例えばByteType()。
function ByteType(const S: string; Index: Integer): TMbcsByteType;
この関数は、Sで指定された文字列のIndexバイト目が、1バイト文字なのか、あるいは2バイト文字の1バイト目/2バイト目なのかを判断します。この関数のUnicodeString版は、Sで指定された文字列のIndexワード目が1ワード文字なのか、サロゲートペアの1ワード目/2ワード目なのかを判断します。「バイトインデックスを指定する訳ではない/バイトの種類を返す訳ではない」事に注意が必要です。
関数の機能からすればByteType()のUnicodeString版は「WordType()」が妥当な所ですが、既存プロジェクトのマイグレーション(移行)の事を考えれば、致し方ない所もあります。
こうした不幸な命名の関数は他にもありますが、「~Byte~」と名の付く関数には「~Element~」という別名を持つものがあります(実際には「~Element~」の別名として「~Byte~」が用意されています...逆ですね)。もしこれらの「~Byte~」関数を利用していたのなら、可読性の面から、今後は「Element系」の関数を利用する事をお勧めします。
MaxLengthがおかしい!
TEdit等のMaxLengthプロパティは、文字数を制限するものではなく、文字構成要素(エレメント)数を制限します。MaxLengthが1の場合には、サロゲートペアや結合文字列を入力する事ができません。
旧来のDelphiに於いてのMaxLengthプロパティは、作成したアプリケーションがXP以降のテーマに対応していれば、「Unicode(UTF-16)としてカウントした文字構成要素(エレメント)数」になり、対応していないか、XP以前のOSで実行されていれば「ANSI としてカウントした文字構成要素(エレメント)数」、つまりバイト数になります。いずれも、入力文字数が制限されている訳ではない事に注意して下さい。
Unicode正規化を行う手段がない!
結合文字列を扱うには、正規化(標準化)は必須条件です。
例えば、Mac OS Xのファイル名にはデフォルトで結合文字列が使われています。そうすると、正規化(標準化)が行えない場合には、DVD等に収録されたファイルの検索処理で困る事になります。Webアプリケーションを作成する際にも、検索等の前処理として正規化(標準化)は必須だと思われます。
残念ながら、Delphi 2009にはUnicode正規化を簡単に行うための関数やクラスは用意されていません。これについての補足も別の記事で触れたいと思っています。
既存プロジェクトをUnicodeアプリケーションにするには?
過去のプロジェクトをUnicodeアプリケーションにマイグレーション(移行)するには幾つかのコツがあります。これについては、既に詳細な情報が公開されていますので、トピックへのリンクを張るだけに留めます。
- Delphi Unicodeワールド パートI:Unicodeとは?なぜ必要なのか?そして、Delphiでどのような作業を行うのか?
- Delphi Unicodeワールド パートII:RTLの新機能と、Unicodeをサポートするクラス
- Delphi Unicodeワールド パートIII:コードをUnicode対応にする
まとめ
以上、Delphi 2009とUnicodeについて駆け足で解説しましたが、いかがだったでしょうか?
Delphi 2009でUnicodeアプリケーションを作るのは、それ程難しい事ではありません。むしろ、簡単に作る事ができます。ただ、旧来のANSI文字列の扱いに慣れてしまっていると、その既成概念から「Unicodeは難しい」であるとか、「アプリケーションをUnicode化するメリットを見出せない」という結論を出してしまいがちです。
Delphi 2009とUnicodeに対する基礎的な知識が身に付けば、「Unicodeアプリケーションを作る」ことのメリットを次第に考えられるようになってきます。今回の一連のUnicode記事が、少しでもあなたのお役に立てたのならば幸いです。