14

プログラムをできるだけ移植可能にしたい。è などのアクセント付き文字の文字列を検索します。これは問題になる可能性がありますか?HTML エンティティに相当する C++ はありますか?

これは、switch ステートメントで使用されます。たとえば、次のようになります。

switch(someChar) //someChar is of type char
{
   case 'é' :
        x = 1;
        break;
   case 'è' :
   ...
}
4

3 に答える 3

11

C ++ソースで非ASCII文字を使用する主な問題は、コンパイラがソースに使用されるエンコーディングを認識している必要があることです。ソースが7ビットASCIIの場合、ほとんどすべてのコンパイラがデフォルトでASCII互換のエンコーディングを想定しているため、通常は問題になりません。

また、すべてのコンパイラがエンコーディングに関して構成可能であるとは限らないため、2つのコンパイラが無条件に互換性のないエンコーディングを使用する可能性があります。つまり、非ASCII文字を使用すると、両方で使用できないソースコードが生成される可能性があります。

  • GCC:ソース、実行、およびワイド実行エンコーディングを設定するためのコマンドラインオプションがあります。デフォルトはロケールによって設定され、最近では通常UTF-8を使用します。
  • MSVC:いわゆる「BOM」を使用してソースエンコーディング(UTF-16BE / LE、UTF-8、およびシステムロケールエンコーディングの間)を決定し、常にシステムロケールを実行エンコーディングとして使用します。編集:VS 2015 Update 2の時点で、MSVCは、UTF-8のサポートを含め、ソース文字セットと実行文字セットを制御するコンパイラスイッチをサポートしています。ここを参照してください
  • Clang:ソースおよび実行エンコーディングとして常にUTF-8を使用します

したがって、検索対象の文字列がUTF-8の場合(おそらく実行文字セットがUTF-8であるため)、アクセント付き文字を検索するコードで何が起こるかを検討してください。文字リテラル'é'が期待どおりに機能するかどうかに関係なく、アクセント付き文字は1バイトで表されないため、アクセント付き文字は見つかりません。代わりに、さまざまなバイトシーケンスを検索する必要があります。


C++が文字および文字列リテラルで許可するさまざまな種類のエスケープがあります。ユニバーサル文字名を使用すると、Unicodeコードポイントを指定でき、その文字がソースに表示されているかのように正確に処理されます。たとえば、\u00E9または\U000000E9

(他の一部の言語は\uU + FFFFまでのコードポイントをサポートする必要がありますが、それを超えるコードポイントに対するC ++のサポートがないか、代理コードポイントを使用するようにします。C++では代理コードポイントを使用できません。代わりに、C++にはすべてのコードポイントを直接サポートする\Uバリアントがあります。 。)

UCNは、文字および文字列リテラルの外部でも機能することになっています。このようなリテラルの外部では、UCNは基本的なソース文字セットに含まれていない文字に制限されています。ただし、最近まで、コンパイラはこの(C ++ 98)機能を実装していませんでした。現在、Clangはかなり完全なサポートを持っているようで、MSVCは少なくとも部分的なサポートを持っているようで、GCCはオプションで実験的なサポートを提供すると主張しています-fextended-identifiers

UCNは、ソースに表示される実際の文字と同じように扱われることになっていることを思い出してください。したがって、UCN識別子を適切にサポートするコンパイラでは、コンパイラのソースエンコーディングが最初に文字をサポートしている限り、実際の文字を使用して識別子を簡単に記述できます。

C++は16進エスケープもサポートしています。これらは\xの後に任意の数の16進数が続きます。16進エスケープは、その値を持つ単一のコードポイントであるかのように単一の整数値を表し、実行文字セットへの変換はその値に対して行われません。エンコーディングに関係なく特定のバイト(またはchar16_t、char32_t、またはwchar_t)の値を表す必要がある場合は、これが必要です。

8進数のエスケープもありますが、UCNや16進数のエスケープほど一般的には役に立ちません。


ISO-8859-1またはcp1252でエンコードされたソースファイルで「é」を使用したときにClangが示す診断は次のとおりです。

warning: illegal character encoding in character literal [-Winvalid-source-encoding]
    std::printf("%c\n",'<E9>');
                       ^

Clangはこれを警告としてのみ発行し、ソースバイトの値を含むcharオブジェクトを直接出力します。これは、UTF-8以外のソースコードとの下位互換性のために行われます。

UTF-8でエンコードされたソースを使用する場合は、次のようになります。

error: character too large for enclosing character literal type
    std::printf("%c\n",'<U+00E9>');
                       ^

Clangは、UTF-8エンコーディングがUnicodeコードポイントU + 00E9に対応し、このコードポイントが単一の文字が保持できる範囲外であることを検出したため、エラーを報告します。(Clangは、それが実行されていたコンソールが非ASCII文字の印刷を処理できないと判断したため、非ASCII文字もエスケープします)。

于 2012-08-17T15:35:50.073 に答える
8

正式には、C++ は識別子においても Unicode のかなり良いサブセットをサポートしているため、理論的には、たとえばantallBlåbærsyltetøyGlass.

実際には、C++ 実装は、識別子で A から Z、数字 0 ~ 9、およびアンダースコアのみをサポートします。一部の実装では、ドル記号 $ も使用できます。ただし、標準ではドル記号は使用できません。

テキスト リテラルで Unicode 文字を指定するには、汎用文字 nameを使用できます。これは名前ではなく、エスケープ シーケンスに似ています (例:\u20ACユーロ記号 €)。ソース コードを UTF-8 として保存すると、そのような文字を直接書き込むこともできます。Visual C++ では、UTF-8 ソース コードを認識するために BOM (バイト オーダー マーク) が必要であることに注意してください。

文字列を UTF-8 でエンコードされたもの (つまり、*nix で一般的な型) として扱う場合char、ASCII 範囲 0...127 の外にある "é" は単一のchar値ではないため、のcaseラベルとして使用できswitchます。

ただし、この特定の文字は、1 文字あたり 1 バイトのエンコーディングである Windows ANSI Western のサブセットである Latin-1 の一部です。そのため、文字列値に ANSI エンコーディングを使用する Windows の西部のインストールでは、単一の値であり、そのように使用できます。Latin-1 も Unicode のサブセット (Unicode の最初の 256 コード ポイントで構成される) であるためwchar_t、たとえば などのベース文字列やstd::wstringUnicode としてのワイド文字列では、"é" も単一の値、つまり in と同じ値になります。 Latin-1 および Windows ANSI Western では。

それでも、 を使用wchar_tして Unicode を表現しても、任意の文字が単一の値になるという保証はありません。

たとえば、Windows では awchar_tはわずか 16 ビットであり、標準エンコーディングは UTF-16 です。この場合、いわゆるBasic Multilingual Plane (元の 16 ビット Unicode) の外部にある文字は、サロゲート ペアと呼ばれる 2 つの値で表されます。さらに悪いことに、UTF-32 Unicode を使用しても、アクセント付き文字を 2 つ以上の値で表すことができます。つまり、最初に基本文字の種類を表す値、次にアクセント記号などを追加してそれを変更する値です。したがって、完全な一般性のために、 32 ビットであっても、文字が単一の値であることに依存しないでwchar_tください。

于 2012-08-16T22:13:44.980 に答える
4

編集: switch ステートメントでマクロを使用するには、元のソリューションに 2 つの変更が必要です。まず、すべての文字が整数型に収まる必要があります。これを確実にする最善の方法は、 でワイド文字を使用することwchar_tです。次に、マクロは文字列リテラルではなく文字リテラルでなければなりません。例えば

#define E_GRAVE L'\u00E8'

wchar_t someChar = ...;
switch(someChar)
{
   case E_GRAVE :
        x = 1;
        break;
   ...
}


完全に移植可能な方法の 1 つは、アクセント付き文字のマクロを定義し、文字列連結に依存することです。

// è (U+00E8) in UTF-8 encoding
#define E_GRAVE "\xC3\xA8"

cout << "Resum" E_GRAVE << endl;

もちろん、これは UTF-8 で作業していることを前提としています。この方法で、任意の文字セットをサポートできます。Windows で UTF-16 を使用する方法は次のとおりです。

#define E_GRAVE L"\u00E8"

wchar_t * resume = L"Resum" E_GRAVE;
于 2012-08-16T22:32:59.077 に答える