13

標準のC++にはcharwchar_t文字を格納するためのとがあります。0x00から0xFFcharまでの値を格納できます。また、との間の値を格納できます。を使用するため、1バイトの文字のみを格納できます。を使用するため、最大2バイト幅の文字を格納できます。これは私がC++の文字列について知っていることです。これまでに何か間違ったことを言ったら訂正してください。wchar_t0x00000xFFFFstd::stringcharstd::wstringwchar_t

ウィキペディアでUTF-8の記事を読んだところ、一部のUnicode文字が最大4バイトのスペースを消費することがわかりました。たとえば、漢字はUnicodeコードポイント0x24B62を持ち、メモリ内で3バイトのスペースを消費します。

この種の文字を処理するためのSTL文字列コンテナはありますか?私はのようなものを探していstd::string32ます。また、main()ASCIIエントリポイントwmain()用、16ビット文字をサポートするエントリポイント用がありました。3バイトおよび4バイトのUnicodeでサポートされているコードにはどのエントリポイントを使用しますか?

小さな例を追加していただけますか?

(私のOS:Windows 7 x64)

4

5 に答える 5

24

まず、Unicodeをよりよく理解する必要があります。あなたの質問に対する具体的な答えは一番下にあります。

コンセプト

プログラミング入門コースで教えられているような非常に単純なテキスト処理に必要なものよりも、より微妙な概念のセットが必要です。

  • バイト
  • コードユニット
  • コードポイント
  • 抽象的な文字
  • ユーザーが認識したキャラクター

バイトは、アドレス可能なメモリの最小単位です。現在は通常8ビットで、最大256の異なる値を格納できます。定義上、charは1バイトです。

コード単位は、テキストの保存に使用されるデータの最小の固定サイズ単位です。テキストの内容をあまり気にせず、テキストをどこかにコピーしたり、テキストが使用しているメモリの量を計算したりする場合は、コード単位を気にします。それ以外の場合、コード単位はあまり使用されません。

コードポイントは、文字セットの個別のメンバーを表します。文字セットに含まれる「文字」が何であれ、それらにはすべて一意の番号が割り当てられ、特定の番号がエンコードされているのを見ると、処理している文字セットのメンバーがわかります。

抽象文字は、言語システムで意味を持つエンティティであり、その表現またはその意味に割り当てられたコードポイントとは異なります。

ユーザーが知覚する文字は、そのように聞こえます。ユーザーが使用している言語システムのキャラクターとして何を考えているか。

昔は、charこれらすべてを表してcharいます。aは定義上バイトであり、char*文字列ではコード単位はcharsであり、文字セットは小さいため、で表現できる256の値はcharすべてのメンバーを表すのに十分であり、サポートされているのはシンプルだったので、文字セットのメンバーは主にユーザーが直接使用したい文字を表しています。

しかし、ほとんどすべてをchar表すこの単純なシステムは、より複雑なシステムをサポートするのに十分ではありませんでした。


遭遇した最初の問題は、一部の言語が256文字をはるかに超える文字を使用することでした。そのため、「ワイド」文字が導入されました。ワイド文字は、上記の4つの概念、コードユニット、コードポイント、抽象文字、およびユーザーが認識する文字を表すために、依然として単一のタイプを使用していました。ただし、ワイド文字は1バイトではなくなりました。これは、大きな文字セットをサポートする最も簡単な方法であると考えられていました。

コードは、。の代わりにワイド文字を処理することを除いて、ほとんど同じである可能性がありcharます。

しかし、多くの言語システムはそれほど単純ではないことがわかりました。一部のシステムでは、ユーザーが認識するすべての文字が、文字セット内の単一の抽象文字で表される必要がないことが理にかなっています。その結果、Unicode文字セットを使用するテキストは、複数の抽象文字を使用してユーザーが知覚する文字を表す場合や、単一の抽象文字を使用して複数のユーザーが知覚する文字を表す場合があります。

ワイド文字には別の問題があります。コードユニットのサイズが大きくなるため、すべての文字に使用されるスペースが増えます。シングルバイトコードユニットで適切に表現できるテキストを処理したいが、ワイド文字のシステムを使用する必要がある場合、使用されるメモリの量は、シングルバイトコードユニットの場合よりも多くなります。そのため、ワイド文字は幅が広すぎないことが望まれました。同時に、ワイド文字は、文字セットのすべてのメンバーに一意の値を提供するのに十分な幅である必要があります。

Unicodeには現在、約100,000の抽象文字が含まれています。これには、ほとんどの人が使用するよりも幅の広いワイド文字が必要であることがわかります。結果として、ワイド文字のシステム。コードポイント値を直接格納するために1バイトを超えるコード単位が使用される場合、望ましくないことがわかります。

要約すると、元々、バイト、コードユニット、コードポイント、抽象文字、およびユーザーが認識する文字を区別する必要はありませんでした。ただし、時間の経過とともに、これらの各概念を区別する必要が生じました。


エンコーディング

上記の前は、テキストデータは簡単に保存できました。すべてのユーザーが認識した文字は、コードポイント値を持つ抽象文字に対応していました。256の値で十分な文字が十分にありませんでした。したがって、ユーザーが認識した目的の文字に対応するコードポイント番号をバイトとして直接格納するだけです。その後、ワイド文字では、ユーザーが認識した文字に対応する値が、たとえば16ビットなどのより大きなサイズの整数として直接格納されました。

しかし、Unicodeテキストをこの方法で保存すると、人々が費やすよりも多くのメモリを使用するため(文字ごとに3または4バイト)Unicodeの「エンコーディング」は、コードポイント値を直接保存するのではなく、リバーシブル関数を使用してテキストを保存します。各コードポイントに格納するコードユニット値の数。

たとえば、UTF-8エンコーディングは、最も一般的に使用されるUnicodeコードポイントを取得し、単一の1バイトコードユニットを使用してそれらを表すことができます。あまり一般的ではないコードポイントは、2つの1バイトコードユニットを使用して格納されます。まだあまり一般的ではないコードポイントは、3つまたは4つのコードユニットを使用して格納されます。

これは、一般的なテキストは通常​​、16ビット幅の文字スキームよりも少ないメモリを使用してUTF-8エンコーディングで格納できることを意味しますが、格納される数値は必ずしも抽象文字のコードポイント値に直接対応するわけではありません。代わりに、どの抽象文字が格納されているかを知る必要がある場合は、格納されているコード単位を「デコード」する必要があります。また、ユーザーが知覚する文字を知る必要がある場合は、抽象文字をさらにユーザーが知覚する文字に変換する必要があります。

さまざまなエンコーディングがあり、それらのエンコーディングを使用してデータを抽象文字に変換するには、正しいデコード方法を知っている必要があります。コードポイント値をコード単位に変換するためにどのエンコーディングが使用されたかがわからない場合、保存された値は事実上無意味です。


エンコーディングの重要な意味は、エンコードされたデータの特定の操作が有効であるか、意味があるかを知る必要があるということです。

たとえば、文字列の「サイズ」を取得したい場合、バイト、コード単位、抽象文字、またはユーザーが認識した文字を数えていますか?std::string::size()コード単位をカウントします。別のカウントが必要な場合は、別の方法を使用する必要があります。

別の例として、エンコードされた文字列を分割する場合、結果がそのエンコードで引き続き有効であり、データの意味が意図せずに変更されていないように分割しているかどうかを知る必要があります。たとえば、同じコードポイントに属するコードユニット間で分割して、無効なエンコーディングを生成する場合があります。または、ユーザーが認識した文字を表すために組み合わせる必要のあるコードポイント間で分割して、ユーザーが正しくないと見なすデータを生成する場合もあります。

回答

現在charwchar_tコードユニットとのみ見なすことができます。1バイトしかないという事実charは、2、3、または4バイトを取るコードポイントを表すことを妨げるものではありません。単に2つ、3つ、または4つcharのを順番に使用する必要があります。これが、UTF-8が機能することを意図した方法です。同様に、wchar_tUTF-16を表すために2バイトを使用するプラットフォームはwchar_t、必要に応じて2バイトを続けて使用するだけです。との実際の値は、Unicodeコードポイントを個別に表すものではcharありwchar_tません。これらは、コードポイントのエンコードから生じるコードユニット値を表します。たとえば、UnicodeコードポイントU + 0400は、UTF-8->で2つのコードユニットにエンコードされます0xD0 0x80。UnicodeコードポイントU+24B62も同様に、4つのコードユニットとしてエンコードされます0xF0 0xA4 0xAD 0xA2

std::stringしたがって、UTF-8でエンコードされたデータを保持するために使用できます。

Windowsではmain()、ASCIIだけでなく、システムcharエンコーディングが何であれサポートします。残念ながら、Windowsはchar他のプラットフォームのようにシステムエンコーディングとしてUTF-8をサポートしていないため、cp1252などのレガシーエンコーディングまたはシステムで使用するように構成されているものに制限されます。main()ただし、 sargcとパラメータを使用する代わりに、Win32API呼び出しを使用してUTF-16コマンドラインパラメータに直接アクセスすることはできargvます。GetCommandLineW()およびを参照してくださいCommandLineToArgvW

wmain()argvパラメータはUnicodeを完全にサポートしています。wchar_tWindowsに格納されている16ビットコードユニットはUTF-16コードユニットです。Windows APIはUTF-16をネイティブに使用するため、Windowsでの操作は非常に簡単です。wmain()ただし、は非標準であるため、これに依存することはできません。

于 2012-09-28T17:59:09.277 に答える
4

のサイズと意味wchar_tは実装によって定義されます。あなたが言うように、Windowsでは16ビットです。Unixライクなシステムでは32ビットであることがよくありますが、常にそうとは限りません。

さらに言えば、コンパイラーは独自のことを実行しwchar_t、システムが言うものとは異なるサイズを選択することが許可されています-それはシステムの他の部分とABI互換ではありません。

C ++ 11はstd::u32string、ユニコードコードポイントの文字列を表すためのを提供します。十分に最近のMicrosoftコンパイラにはそれが含まれていると思います。Microsoftのシステム関数は、32ビットのユニコードコードポイント(別名UTF-32、UCS-4)ではなく、16ビット幅の文字(別名UTF-16le)を想定しているため、使用は多少制限されます。

ただし、UTF-8について言及している場合:UTF-8でエンコードされたデータは通常のに格納できますstd::string。もちろん、これは可変長エンコーディングであるため、インデックスでユニコードコードポイントにアクセスすることはできません。インデックスでバイトにアクセスすることしかできません。ただし、通常は、を使用している場合でも、インデックスによってコードポイントにアクセスする必要がないようにコードを記述しますu32string。Unicodeコードポイントは、Unicodeに結合マークが存在するため、印刷可能な文字(「書記素」)と1-1に対応していません。そのため、プログラミングを学習するときに文字列を操作する小さなトリックの多く(文字列を逆にして、部分文字列を検索する)何に保存しても、Unicodeデータを簡単に操作することはできません。

あなたが言うように、キャラクターは\u24B62です。これは、3つではなく一連の4バイトとしてエンコードされたUTF-8です:F0 A4ADA2。UTF-8でエンコードされたデータとユニコードコードポイントの間の変換は手間がかかります(確かに、それほど多くの手間はかからず、ライブラリ関数がそれを行います)。「エンコードされたデータ」と「ユニコードされたデータ」は別々のものと見なすのが最善です。たとえば、テキストを画面にレンダリングする必要があるところまで、最も便利な表現を使用できます。その時点で、出力先が理解できるエンコーディングに(再)エンコードする必要があります。

于 2012-09-28T16:14:46.477 に答える
4

Windows はUTF-16を使用します。U+0000 から U+D7FF および U+E000 から U+FFFF の範囲のコード ポイントは直接格納されます。これらの範囲外のものは、UTF-16 エンコード規則に従って 2 つの 16 ビット値に分割されます。

たとえば、0x24B62 は 0xd892,0xdf62 としてエンコードされます。

任意の方法で文字列を操作するように変換できますが、Windows API は引き続き UTF-16 を必要とし、提供するため、おそらく最も便利です。

于 2012-09-28T16:20:26.000 に答える
3

標準 C++ には、文字を格納するための char と wchar_t がありますか? char は、0x00 から 0xFF までの値を格納できます。また、wchar_t は 0x0000 から 0xFFFF までの値を格納できます。

そうではありません:

sizeof(char)     == 1   so 1 byte per character.
sizeof(wchar_t)  == ?   Depends on your system 
                        (for unix usually 4 for Windows usually 2).

Unicode 文字は最大 4 バイトのスペースを消費します。

そうではありません。Unicode はエンコーディングではありません。Unicode は、各コード ポイントが何であるかを定義する標準であり、コード ポイントは 21 ビットに制限されています。最初の 16 ビットはコード プレーン上の文字位置を定義し、次の 5 ビットは文字がどのプレーン上にあるかを定義します。

いくつかの Unicodeエンコーディングがあります(UTF-8、UTF-16、および UTF-32 が最も一般的です)。これは、文字をメモリに格納する方法です。3つの間には実際的な違いがあります。

    UTF-8: 保管と持ち運びに最適 (コンパクトなので)
             可変長だからダメ
    UTF-16: ほぼすべての点でひどい
             常に大きく、可変長です
             (BMP にないものは、サロゲート ペアとしてエンコードする必要があります)
    UTF-32: 固定サイズであるため、インメモリ表現に最適
             通常はやり過ぎである各文字に4バイトかかるため、悪い

個人的には、トランスポートとストレージには UTF-8 を使用し、テキストのメモリ内表現には UTF-32 を使用します。

于 2012-09-28T16:26:58.530 に答える
1

charwchar_tテキスト文字列に使用されるデータ型は および だけではありません。C++11 では、新しいchar16_tchar32_tデータ型、および のそれぞれの STLstd::u16stringstd::u32stringtypedef が導入され、さまざまなプラットフォームでさまざまなサイズとエンコーディングを持つ型 std::basic_stringのあいまいさに対処しています。一部のプラットフォームでは 16 ビットであり、UTF-16 エンコーディングに適していますが、他のプラットフォームでは 32 ビットであり、代わりに UTF-32 エンコーディングに適しています。 すべてのプラットフォームで、具体的には16 ビットおよび UTF-16 であり、具体的には 32 ビットおよび UTF-32 です。wchar_twchar_tchar16_tchar32_t

于 2012-10-03T06:37:08.787 に答える