UDN
Search public documentation:

CharacterEncodingJP
English Translation
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

文字のエンコード

ドキュメントの概要:文字セット、および、Unrealによって使用された場合に起きるエンコード上の問題に関する概要

ドキュメントの変更ログ:作成者:Jack Porter、更新者:Jun Shimoda.

はじめに

このドキュメントでは、Unrealで使用されるキャラクターのエンコーディングについて概説します。

あらかじめ必要となる知識:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

 内部表現

Unreal Engine 3の文字列はすべて、メモリの中に、UTF-16フォーマットで、FStrings配列またはTCHAR配列として格納されています。多くのコードでは、2バイトが1コードポイントとして想定されているため、基本多言語プレーン(Basic Multilingual Plane: BMP)にのみ対応しています。そのため、Unrealの内部エンコーディングは、UCS-2として記述されるほうがより正確になものになります。文字列は、現在のプラットフォームに合うバイトオーダーで格納されます。

パッケージにシリアル化する場合や、ディスクにまたはディスクからシリアル化する場合、またはネットワークの送受信でシリアル化する場合は、0xffより小さいTCHAR文字はすべて、8ビットバイトの列として格納されます。それ以外は、2バイトのUTF-16として格納されます。シリアル化コードでは、必要に応じて、どのようなエンディアン変換も処理できます。

UE3によってロードされるテキストファイル

Unrealが外部のテキストファイルを読み込むとき(たとえば、.INTファイルを実行時に読み込むときやUnrealScriptコンパイラがUnrealScript .ucソースファイルを読み込むときなど)は、ほとんどの場合、UnMisc.cppにあるappLoadFileToString()関数によって処理されます。主要な処理は、appBufferToString()関数で行います。

この関数は、UTF-16ファイルにあるUnicodeのバイトオーダーマーク(BOM)を読み取り、もしBOMがあれば、そのファイルをUTF-16ファイルとして、ビッグまたはリトルのエンディアンで読み込みます。

BOMが存在しない場合の動作は、プラットフォームに依存します。

Windowsでは、デフォルトのWindows MBCSエンコーディングを使用してテキストをUTF-16に変換しようとします(デフォルトのエンコーディングの例:米国英語および西ヨーロッパのバージョンではWindows-1252、韓国語バージョンではCP949、日本語バージョンではCP932)。また、MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS...)が使用されます。これは、2009年7月ごろのQAビルドで追加されました。

変換に失敗した場合やWindows以外のプラットフォームの場合は、関数は単にそれぞれのバイトを読み込み、読み込んだものを16ビットにパッドすることによってTCHARの配列を作ります。

なお、appLoadFileToString()関数でロードされた、UTF-8でエンコードされたテクスチャファイルを検出またはデコードするコードはありません。

Unrealによって保存されたテキストファイル

このエンジンによって生成されるテキストファイルの多くは、appSaveStringToFile()関数によって保存されます。

TCHAR型の文字がすべてシングルバイトで表されている文字列は、8ビットバイト列として格納されます。そのほかの場合は、bAlwaysSaveAsAnsiフラグがTRUEとならない場合にUTF-16として格納されます。その場合、まず、デフォルトのWindowsエンコーディング形式に変換されます。現在これは、シェーダファイルでしか実行されません。それによって、シェーダコンパイラがかかえるUTF-16ファイルに関する問題を回避することができます。

Unrealによって使用されるテキストファイルに推奨されるエンコーディング

INT と INI ファイル

どちらかのエンディアンの UTF-16 です。アジア言語のデフォルト MBCS 文字コード (例:CP932) は Windows で動作しますが、これらのファイルは PS3 や Xbox360 で読み込まれる必要があり、変換コードは Windows でしか実行することができません。

ソース コード

一般的に、C++ または UnrealScript ソース コード内の文字列リテラルを推奨せず、このデータは INT ファイルに入れることを推奨します。

UnrealScript ソース コード

UTF-16、またはデフォルトの Windows 文字コードです。どちらの場合も、コンパイラは Windows で実行され、内部で UTF-16 でテキストを格納する Unreal パッケージを生成するので、日本語と韓国語の文字列リテラルとコメントは、正常に動作するはずです。デフォルト Windows 文字コードを使用した場合、これらの .uc ファイルは、異なるロケールを持つマシンでは動作しないことに注意してください。

C++ ソース

UTF-8、またはデフォルトの Windows 文字コードです。MSVC、Xbox360 コンパイラ、gcc はすべて、UTF-8 で文字コードされたソース ファイルで問題ないはずです。例えば著作権、商標、「度」のシンボルのような高いビット セットの文字を持つ Latin-1 で文字コードされたファイルは、ソース コードでは可能な限り避けるべきです。これは、異なるロケールを持つシステム上で文字コードが壊れるためです。サード パーティのソフトウェアでのいくつかの事例は回避不可能 (例:著作権表示) なので、MSVC に関しては、警告 4819 を無効化します。これは、アジアの Windows でコンパイルを行う際に起こる警告です。

Perforce での UTF-16 テキスト ファイルの格納

  • 'Text' を使用 しない でください。
    • UTF-x ファイルがチェックインされており、テキストとして格納されていると、同期後に破損します。
  • 'Binary' を使用する場合、ファイルを排他的チェックアウトとしてマークしてください。
    • ASCII、UTF-8、UTF-16 をチェックインすることができ、これはエンジンで動作します。
    • しかしながら、バイナリ ファイルはマージすることができないので、ファイルが排他的チェックアウトとマークされていない場合、変更は無視されます。
  • 'UTF-16' を使用する場合、UTF-16 でないファイルがチェックインされないようにしてください。
    • 非 UTF-16 を UTF-16 としてチェックインすることを許可しないような Perforce トリガがあります。
      • //depot/UnrealEngine3/Development/Tools/P4Utils/CheckUTF16/
  • 'Unicode' タイプは UTF-8 であり、これといった使い道はありません。

変換ルーチン

さまざまなコードから、またさまざまなコードへ文字列を変換する多くのマクロがあります。これらのマクロは、ローカル スコープで宣言されたクラス インスタンスを使用し、スタック上でスペースを割り当てるため、これらにポインタを維持させないことが非常に重要です。文字列を関数呼び出しへパスするのみに意図されています。

  • TCHAR_TO_ANSI(str)
  • TCHAR_TO_OEM(str)
  • ANSI_TO_TCHAR(str)
  • TCHAR_TO_UTF8(str)
  • UTF8_TO_TCHAR(str)

これらは UnStringConv.h からの以下のヘルパ クラスを使用します。

  • typedef TStringConversion<TCHAR,ANSICHAR,FANSIToTCHAR_Convert> FANSIToTCHAR;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToANSI_Convert> FTCHARToANSI;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToOEM_Convert> FTCHARToOEM;
  • typedef TStringConversion<ANSICHAR,TCHAR,FTCHARToUTF8_Convert> FTCHARToUTF8;
  • typedef TStringConversion<TCHAR,ANSICHAR,FUTF8ToTCHAR_Convert> FUTF8ToTCHAR;

また、TCHAR_TO_ANSI を使用する際には、TCHAR 文字列の長さとバイト数が同じになると推測しないことが重要です。複数バイトの文字セットは、1 つの TCHAR 文字に対して複数バイト必要な場合があります。結果的な文字列の長さをバイトで知りたい場合は、マクロの代わりにヘルパ クラスを使用することができます。以下はその例です。

FString String;
...
FTCHARToANSI Convert(*String);
Ar->Serialize((ANSICHAR*)Convert, Convert.Length());  // FTCHARToANSI::Length() は、空文字を除いた文字コード文字列のバイト数を返します。

C++によるソースコードに関する注意事項のうち東アジア系言語のエンコーディングに特有のものに関する注意事項

UTF-8およびデフォルトのWindowsエンコーディングでは、C++コンパイラに以下のような問題が生じる可能性があります。

デフォルトのWindowsによるエンコーディング
CP932(日本語)、CP936(簡体字中国語)、CP950(繁体字中国語)などの東アジア系言語のダブルバイト文字エンコード形式がソースコードに含まれている場合は、シングルバイト文字のコードページ(米国のCP437など)を使用して動作するWindows上でC++によるソースコードをコンパイルする際に注意が必要です。

これらの東アジア系文字のエンコードシステムでは、最初の1バイトには0x81から0xFEまでが使用され、2番目のバイトには0x40から0xFEまでが使用されます。2番目のバイトにおける0x5Cという値は、ASCII/latin-1ではバックスラッシュとして処理されます。バックスラッシュは、C++において特別な意味をもちます(文字列リテラル内ではエスケープシークエンスの意味。また、行末で使用された場合は、行の継続を意味します。)。
そのようなソースコードを、シングルバイトコードページをもつWindowsでコンパイルする場合、コンパイラは、東アジア系言語のダブルバイト文字のエンコード形式が無視されます。その結果、コンパイルエラーが起きるか、最悪の場合はEXEファイルでバグが発生します。

シングルラインコメントにおいて
東アジア系言語によるコメントに0x5cが入っている場合は、行の欠落が生じるために、発見するのが難しいバグやエラーが生じる可能性があります。

    // EastAsianCharacterCommentThatContains0x5cInTheEndOfComment0x5c'\'
    important_function(); /* this line would be connected to above line as part of comment (この行は上の行とつながり、コメントの一部となってしまいます) */

文字列リテラル内において
0x5cエスケープシーケンスとして認識するために、文字列の破損またはエラーが生じる可能性があります。

    printf("EastAsianCharacterThatContains0x5c'\'AndIfContains0x5cInTheEndOfString0x5c'\'");
    function();
    printf("Compiler recognizes left double quotation mark in this line as the end of string literal that continued from first line, and expected this message is C++ code.");
0x5cに続く文字が実際にエスケープシーケンスを指定する場合、コンパイラは、このエスケープシーケンス文字のセットを指定された1つの文字に変換します。
(エスケープシーケンスを指定しない場合は、動作結果は実装時の定義に依存することになります。ただし、MSVC(Microsoft Visual C++)では、0x5cが取り除かれ、"unrecognized character escape sequence" (エスケープシーケンスとして正しく認識できません)という警告が表示されます。)
上記の例では、文字列の最後に0x5cバックスラッシュがあり、次の文字がダブルクオーテーションマークです。そのため、このエスケープシーケンス「\"」は、文字列データの中で1つのダブルクオーテーションマークに変換され、コンパイラは次のダブルクオーテーションマークが出てくるか、ファイルの終わりに達するまで、文字列データが生成され続け、エラーが発生します。

危険な文字の例:
CP932 (日本語 シフトJIS)において、「表」という文字のコードは、0x955Cです。CP932では、多くの文字に0x5Cが入っています。
CP936 (簡体字中国語 GBK)において、「乗」という文字は、0x815Cです。CP936では、多くの文字に0x5Cが入っています。
CP950 (繁体字中国語 Big5)において、「功」という文字は、0xA55Cです。CP950では、多くの文字に0x5Cが入っています。
CP949 (韓国語 EUC-KR)は問題ありません。EUC-KRでは、第2バイトに0x5Cが使用されないためです。

BOMがついていないUTF-8 (エディタによってはBOMがsignature(シグニチャ/署名)と呼ばれる)
ソースコードで、東アジア系言語の文字がUTF-8として保存されている場合は、C++のソースコードを、CP949 (韓国語)、CP932 (日本語)、CP936 (簡体字中国語)、CP950 (簡体字中国語)の東アジア言語系コードページを使うWindows上でコンパイルする際に注意が必要です。

UTF-8文字エンコーディングでは、東アジア言語系文字の場合に3バイトが使用されます。0xE0から0xEFまでが第1バイトに、0x80から0xBFまでが第2バイトに、0x80から0xBFまでが第3バイトに割り当てられています。BOMが付いていない場合、東アジア言語系Windowsのデフォルトのエンコーディングでは、UTF-8でエンコードされた3バイトとその次に続く1バイトを、2バイトの東アジア系エンコード文字が2つあるものとして認識してしまいます。具体的には、第1バイトと第2バイトを合わせて第1の東アジア系文字として認識し、第3バイトとその後に続く1バイト分を2つ目の東アジア系文字として認識するのです。
問題が発生する可能性があるのは、UTF-8でエンコードされた3バイトに続く文字が、文字列リテラルにおいて特別な意味がある場合です。

(例) インラインコメントにおいて
コメント文を構成する東アジア系文字が奇数個あり、かつ、次に続く文字がコメント終了の記号である場合、コードが欠落してしまうため、発見しづらいバグやエラーが生じます。

    /*OddNumberOfEastAsianCharacterComment*/
    important_function();
    /*normal comment*/
東アジア系言語のコードページを持つWindows上では、コンパイラが、UTF-8でデコードされた東アジア系文字からなるコメントの最後に置かれた1バイトとアスタリスク(*)を、1つの東アジア系文字として認識し、その次の文字もコメントの一部として扱ってしまいます。上記の例では、コンパイラは、important_function()関数をコメントの一部として除去してしまうのです。
この動作はたいへん危険なものでありながら、同時に、この欠落したコードを発見することは難しいのです。

シングルラインコメントにおいて
バックラッシュ(\)が東アジア系言語によるコメントの最後に置かれた場合、行が欠落するため、発見が難しいバグやエラーが発生します。

    // OddNumberOfEastAsianCharacterComment\
    description(); /* coder intended this line as comment, by using backslash at the end of above line (プログラマは、上の行の終わりにバックスラッシュを置くことによって、この行をコメント化しています。) */
これはとても珍しいケースです。なぜなら、プログラマは、コメントの最後にバックラッシュ(\)をあえて置くようなことをしないからです。

文字列リテラルの内部で
文字列リテラル内に奇数個の東アジア系文字があり、次に続く文字が特別な意味をもつ記号である場合は、文字列が破損してエラーや警告が発生します。

    printf("OddNumberOfEastAsiaCharacterString");
    printf("OddNumberOfEastAsiaCharacterString%d",0);
    printf("OddNumberOfEastAsiaCharacterString\n");
東アジア系言語のコードページを使うWindowsでは、コンパイラが、UTF-8でデコードされた東アジア系文字からなる文字列で最後に置かれた1バイトとその次に置かれた1バイトを、1つの東アジア系文字として認識してしまいます。運よく、コンパイラ警告C4819(無効にしていない場合)やエラーによって問題に気がつくこともあります。そうでない場合は、文字列が破損してしまいます。

結論
UTF-8によるエンコーディングまたはデフォルトのWindowsによるエンコーディングを使用することはできますが、上記の問題について注意する必要があります。繰り返しになりますが、C++またはUnrealScriptスクリプト内で文字列リテラルを使用することは推奨しません。C++ソースコード内で東アジア系文字のエンコーディングを使用しなければならない場合は、必ず、デフォルトのコードページとして東アジア系のコードページを使用してください。
もうひとつ適切な方法としては、BOM付きUTF-8の使用があげられます。(BOMのことをUnicode署名としているテキストエディタもあります。)

注記
2010年2月18日に、UTF-8およびUTF-16に関していくつかのコンパイラでテストしてみました。

PC用およびXbox 360用のMSVCや、PS3用のgccまたはslcでは、UTF-8でエンコードされたソースコード(BOMありとBOMなしの両方)をコンパイルすることができました。 しかし、UTF-16(リトルエンディアンとビッグエンディアンともに)は、MSVCのみが対応しました。

Perforceは、UTF-16とUTF-8の両方で機能しました。ただしp4 diffコマンドは、UTF-8ファイルに含まれているBOMの文字を可視化してしまいます。

外部の参照先:Code Pages Supported by Windows

以下も参照してください。