web-dev-qa-db-ja.com

wchar_tは正確に何を表すことができますか?

cppreference.comのwchar_tに関するドキュメント によると:

wchar_t-ワイド文字表現のタイプ(ワイド文字列を参照)。サポートされているすべての文字コードポイントを表すのに十分な大きさである必要があります(Unicodeをサポートするシステムでは32ビット。注目すべき例外はWindowsで、wchar_tは16ビットで、UTF-16コード単位を保持します)サイズ、符号、配置が同じです。整数型の1つですが、特殊な型です。

標準は [basic.fundamental]/5 で述べています:

タイプwchar_­tは、サポートされているロケール間で指定された最大の拡張文字セットのすべてのメンバーの個別コードを表す値を持つ個別タイプです。タイプwchar_­tは、その基礎となるタイプと呼ばれる他の整数タイプの1つと同じサイズ、符号付き、および位置合わせ要件を持っている必要があります。型char16_­tおよびchar32_­tは、uint_­least16_­t内のuint_­least32_­tおよび<cstdint>とそれぞれ同じサイズ、符号付き、および整列の異なる型を示し、基になる型と呼ばれます。

したがって、Unicode文字を処理する場合は、wchar_tを使用する必要がありますか?

同様に、特定のユニコード文字が "supported" by wchar_tであるかどうかを知る方法は?

22
YSC

したがって、Unicode文字を処理する場合は、wchar_tを使用する必要があります

まず第一に、エンコーディングは特定の文字を表すために特定のタイプを使用することを強制しないことに注意してください。 charを使用して、wchar_tと同じようにUnicode文字を表すことができます。最大4つのcharsを一緒に使用すると、UTF-8、UTF-16、またはUTF-32エンコーディングに応じて有効なコードポイントが形成されます。一方、wchar_tは1(Linuxの場合はUTF-32)または一緒に機能する最大2(Windowsの場合はUTF-16)を使用できます。

次に、明確なUnicodeエンコーディングはありません。一部のUnicodeエンコーディングは、コードポイント(UTF-32など)を表すために固定幅を使用しますが、その他(UTF-8やUTF-16など)は可変長です(たとえば、文字「a」は確かに1バイトしか使用しませんが、英語のアルファベットから、他の文字は表現のためにより多くのバイトを使います。

したがって、表現する文字の種類を決定し、それに応じてエンコードを選択する必要があります。表現する文字の種類に応じて、これはデータが取るバイト数に影響します。例えば。 UTF-32を使用してほとんど英語の文字を表すと、0バイトが多くなります。 UTF-8は多くのラテン語ベースの言語に適していますが、UTF-16は通常、東アジア言語に適しています。

これを決定したら、コンバージョンの量を最小限に抑え、決定と一貫性を保つ必要があります。

次のステップでは、データを表すのに適切なデータ型(または必要な変換の種類)を決定できます。

コードポイントベースでテキスト操作/解釈を行いたい場合、charは確かに行く方法ではありません。日本語の漢字。ただし、データをやり取りしてそれを定量的なバイトシーケンスと見なしたくない場合は、charを使用します。

TF-8 everywhere へのリンクは既にコメントとして投稿されていますので、そちらもご覧になることをお勧めします。もう1つの読み物は すべてのプログラマーがエンコーディングについて知っておくべきこと です。

現在のところ、C++での初歩的な言語サポートは、Unicodeの場合のみです(char16_tおよびchar32_tデータ型、u8/u/Uリテラルプレフィックスなど)。したがって、エンコーディング(特に変換)を管理するためのライブラリを選択することは確かに良いアドバイスです。

13
Jodocus

_wchar_t_は、UTF16-LE形式を使用するWindowsで使用されます。 _wchar_t_にはワイド文字関数が必要です。たとえば、wcslen(const wchar_t*)ではなくstrlen(const char*)および_std::wstring_の代わりに_std::string_

Unixベースのマシン(Linux、Macなど)はUTF8を使用します。これは、ストレージにcharを使用し、strlen(const char*)や_std::string_などのASCII用の同じCおよびC++関数を使用します(_std::find_first_of_に関する以下のコメントを参照)

_wchar_t_は、Windowsでは2バイト(UTF16)です。しかし、他のマシンでは4バイト(UTF32)です。これは物事をより混乱させます。

UTF32の場合、異なるシステムで同じである_std::u32string_を使用できます。


UTF8をUTF32に変換することを検討してください。これにより、各文字が常に4バイトになり、文字列操作がより簡単になると考えるかもしれません。しかし、それが必要になることはめったにありません。

UTF8は、ASCII 0〜128の文字が他のUnicodeコードポイントを表すために使用されないように設計されています。これには、エスケープシーケンス_'\'_、printf形式指定子、および_,_などの一般的な解析文字

次のUTF8文字列を考えます。コンマを見つけたいとしましょう

_std::string str = u8"汉,????"; //3 code points represented by 8 bytes
_

ASCIIカンマの値は_44_であり、strには値が_44_の1バイトのみが含まれることが保証されています。カンマを見つけるには、 CまたはC++の任意の標準関数を使用して_','_を探すことができます

__を見つけるには、文字列_u8"汉"_を検索できます。このコードポイントは単一の文字として表すことができないためです。

一部のCおよびC++関数は、UTF8ではスムーズに動作しません。これらには

_strtok
strspn
std::find_first_of
_

上記の関数の引数は文字のセットであり、実際の文字列ではありません。

したがって、str.find_first_of(u8"汉")は機能しません。 _u8"汉"_は3バイトであり、_find_first_of_はこれらのバイトのいずれかを検索します。これらのバイトの1つが別のコードポイントを表すために使用される可能性があります。

一方、検索引数のすべての文字はASCII(str自体に任意のUnicode文字を含めることができる)であるため、str.find_first_of(u8",;abcd")は安全です。

まれに、UTF32が必要になる場合があります(ただし、どこにいるのかは想像できませんが)_std::codecvt_を使用して、UTF8をUTF32に変換し、次の操作を実行できます。

_std::u32string u32 = U"012汉"; //4 code points, represented by 4 elements
cout << u32.find_first_of(U"汉") << endl; //outputs 3
cout << u32.find_first_of(U'汉') << endl; //outputs 3
_

サイドノート:

"UTF8 everywhere"ではなく、 "Unicode everywhere"を使用する必要があります。

Linux、Macなどでは、UnicodeにUTF8を使用します。

Windowsでは、UnicodeにUTF16を使用します。 WindowsプログラマはUTF16を使用しますが、UTF8との間で無意味な変換を行いません。しかし、WindowsでUTF8を使用する正当な場合があります。

Windowsプログラマーは、ファイルやWebページなどの保存にUTF8を使用する傾向があります。そのため、互換性の点でWindows以外のプログラマーにとってはそれほど心配する必要はありません。

言語自体は、どのUnicode形式を使用するかは関係ありませんが、実用性の観点からは、作業しているシステムに一致する形式を使用します。

8

したがって、Unicode文字を処理する場合は、wchar_tを使用する必要がありますか?

それはあなたが扱っているエンコーディングに依存します。 UTF-8の場合、charとstd :: stringで十分です。 UTF -8は、最小のエンコード単位が8ビットであることを意味します。U+ 0000からU + 007FまでのすべてのUnicodeコードポイントは、1バイトだけでエンコードされます。コードポイントU + 0080から始まり、UTF-8は2バイトをエンコードに使用し、U + 0800から3バイト、U + 10000から4バイトを使用します。この可変幅(1バイト-2バイト-3バイト-4バイト)を処理するには、charが最適です。 strlenのようなC関数はバイトベースの結果を提供することに注意してください。「öö」は実際には2文字のテキストですが、「ö」は0xC3B6にエンコードされるため、strlenは4を返します。

UTF -16は、最小のエンコード単位が16ビットであることを意味します。U+ 0000からU + FFFFまでのすべてのコードポイントは2バイトでエンコードされます。 U + 100000から始まる4バイトが使用されます。 UTF-16の場合、wchar_tとstd :: wstringを使用する必要があります。これは、遭遇するほとんどの文字が2バイトでエンコードされるためです。 wchar_tを使用すると、strlenなどのC関数を使用できなくなります。 wcslenのような同等のワイド文字を使用する必要があります。

Visual Studioを使用して構成 "Unicode"でビルドすると、UTF-16が得られます。TCHARとCStringは、charではなくwchar_tに基づいています。

5
testalucida

それはすべて、「対処する」という意味によって異なりますが、1つ確かなのは、Unicodeが関係する場合、_std::basic_string_は実際の機能をまったく提供しないということです。

特定のプログラムでは、X個のUnicode対応操作を実行する必要があります。インテリジェントな文字列照合、大文字と小文字の折りたたみ、正規表現、Wordの改行の検索、パス名としてのUnicode文字列の使用など。

これらの操作をサポートすることは、ほとんどの場合、プラットフォームによって提供されるある種のライブラリーおよび/またはネイティブAPIであり、私にとっての目標は、これらの操作が知識を分散することなく実行できるような方法で文字列を格納および操作することです基礎となるライブラリとネイティブAPIは、コード全体で必要以上にサポートされています。また、気が変わった場合に備えて、文字列に格納する文字の幅を将来にわたって保証したいと思っています。

たとえば、 [〜#〜] icu [〜#〜] を使用して重い作業を行うことにしたとします。すぐに明らかな問題があります。_icu::UnicodeString_は、_std::basic_string_とはまったく関係がありません。何をすべきか?コード全体で_icu::UnicodeString_のみを使用しますか?おそらく違います。

あるいは、アプリケーションの焦点がヨーロッパの言語からアジアの言語に切り替わるため、UTF-16が(おそらく)UTF-8よりも優れた選択肢になるでしょう。

したがって、私の選択は、次のような_std::basic_string_から派生したcustom文字列クラスを使用することです。

_typedef wchar_t mychar_t;  // say

class MyString : public std::basic_string <mychar_t>
{
...
};
_

すぐに、コンテナーに格納するコードユニットのサイズを柔軟に選択できます。しかし、それだけではありません。たとえば、上記の宣言(および_std::basic_string_に転送するために提供する必要があるさまざまなコンストラクターのボイラープレートを追加した後)では、まだ次のように言うことはできません。

_MyString s = "abcde";
_

「abcde」は幅の狭い文字列であり、_std::basic_string <wchar_t>_のさまざまなコンストラクターはすべて幅の広い文字列を想定しているためです。マイクロソフトはこれをマクロ(TEXT ("...")または__T ("..."))で解決しますが、それは面倒です。ここで必要なのは、MyStringに適切なコンストラクターをシグネチャMyString (const char *s)とともに提供することだけで、問題は解決しました。

実際には、このコンストラクターは、MyStringに使用される基本的な文字幅に関係なく、おそらくUTF-8文字列を想定し、必要に応じて変換します。コード内のUTF-8リテラルから文字列を作成できるように、文字列をUTF-8として保存する必要があると、どこかで誰かがコメントしています。さて、私たちはその制約を破りました。文字列の基になる文字幅は、好きなようにすることができます。

このスレッドで人々が話しているもう1つのことは、_find_first_of_がUTF-8文字列(および実際には一部のUTF-16文字列)に対して正しく機能しない可能性があることです。さて、今あなたは仕事を適切に行う実装を提供することができます。約30分かかります。 _std::basic_string_に他の「壊れた」実装がある場合(そして確かにあると思います)、それらのほとんどはおそらく同じような簡単さで置き換えることができます。

その他については、MyStringクラスに実装する抽象化のレベルに主に依存します。たとえば、アプリケーションがICUへの依存関係に満足している場合、_icu::UnicodeString_との間で変換するメソッドをいくつか提供するだけで済みます。それはおそらくほとんどの人がすることです。

または、ネイティブWindows APIとの間でUTF-16文字列をやり取りする必要がある場合、_const WCHAR *_との間で変換するメソッドを追加できます(これも、mychar_tのすべての値に対して機能するように実装します)。 。または、さらに進んで、使用しているプラ​​ットフォームとライブラリが提供するUnicodeサポートの一部またはすべてを抽象化することもできます。たとえば、Macは豊富なUnicodeサポートを備えていますが、Objective-Cからしか利用できないため、ラップする必要があります。コードの移植性によって異なります。

そのため、_std::basic_string_として文字列を持ち運ぶ機能を失うことなく、作業の進行に応じて、必要に応じて任意の機能を追加できます。なんとなく。幅がわかっている、または サロゲートペア が含まれていないと想定するコードを記述しないようにしてください。

4
Paul Sanders

まず、(質問で指摘したように)WindowsおよびVisual Studio C++を使用していて、_wchar_t_が16ビットであるかどうかを確認する必要があります。その場合、完全なUnicodeサポートを使用するには、次のことを行う必要があります。 UTF-16エンコーディングを想定しています。

ここでの基本的な問題は、使用している_sizeof wchar_t_ではありませんが、使用するライブラリの場合は、完全なUnicodeサポートをサポートしてください。

charタイプが16ビット幅であるため、Javaにも同様の問題があり、アプリオリは完全なUnicodeスペースをサポートできませんでしたが、それは、それはUTF-16エンコーディングを使用し、ペアのサロゲートが完全な24ビットコードポイントに対処するためです。

UNICODEがハイプレーンのみを使用して、通常は毎日使用されないまれなコードポイントをエンコードすることにも注意する必要があります。

とにかくユニコードをサポートするには、ワイド文字セットを使用する必要があるため、_wchar_t_から始めるのがよいでしょう。 Visual Studioを使用する場合は、ライブラリがUnicode文字をどのように処理するかを確認する必要があります。

注意すべきもう1つの点は、標準ライブラリはロケールサポートを追加する場合にのみ文字セット(これにはUnicodeも含まれます)が処理されるということです(これにはsetlocale(3)などのいくつかのライブラリを初期化する必要があるため)。 setlocale(3)を呼び出していない場合のユニコード(基本的なASCIIのみ)。

_stdio.h_ sを処理するために、ほとんどすべてのstr*(3)関数、およびすべての_wchar_t_ライブラリ関数にワイド文字関数があります。 _/usr/include/wchar.h_ファイルを少し掘り下げると、ルーチンの名前が明らかになります。それらのドキュメントについては、マニュアルページにアクセスしてください:fgetws(3)fputwc(3)fputws(3)fwide(3)fwprintf(3)、 ...

最後に、Microsoft Visual C++を扱っている場合は、最初から実装が異なることを再度考慮してください。それらが完全に標準に準拠しているに対応していても、異なる実装を持つことのいくつかの特異性に対応する必要があります。おそらく、いくつかの用途では異なる関数名を持っているでしょう。

3
Luis Colorado