Delphi 2009 とUnicode : Part III

投稿者:: Hideaki Tominaga

概要: 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” に関する資料は "JIS X 0213:2004 / Unicode 実装ガイド(Microsoft)" としてまとめられていますので、御一読下さい。

サロゲートペアに関する問題

サロゲートペアを表示するための設定

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 2009 IDE では、サロゲートペアを表示できません。これは、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 2009 と Unicode について駆け足で解説しましたが、いかがだったでしょうか?

Delphi 2009 で Unicode アプリケーションを作るのは、それ程難しい事ではありません。むしろ、簡単に作る事ができます。ただ、旧来の ANSI 文字列の扱いに慣れてしまっていると、その既成概念から「Unicode は難しい」であるとか、「アプリケーションを Unicode 化するメリットを見出せない」という結論を出してしまいがちです。

Delphi 2009 と Unicode に対する基礎的な知識が身に付けば、「Unicode アプリケーションを作る」ことのメリットを次第に考えられるようになってきます。今回の一連の Unicode記事が、少しでもあなたのお役に立てたのならば幸いです。

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


関連記事:


公開日:: 10/21/2008 2:40:09 AM

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

Copyright© 1994 - 2013 Embarcadero Technologies, Inc. All rights reserved.