web-dev-qa-db-ja.com

C ++のwchar_tとwstringsの「誤り」とは何ですか?ワイド文字の代替手段は何ですか?

私はC++コミュニティ(特にfreenodeの## c ++)でwstringswchar_t、およびWindows APIでの使用。正確に「間違っている」wchar_twstring、および国際化をサポートしたい場合、ワイド文字の代替手段は何ですか?

80
Ken Li

Wchar_tとは何ですか?

wchar_tは、すべてのロケールのcharエンコードを、すべてのwchar_tが正確に1つのコードポイントを表すwchar_t表現に変換できるように定義されています。

タイプwchar_tは、サポートされているロケール(22.3.1)で指定されている最大の拡張文字セットのすべてのメンバーの個別のコードを表すことができる特殊タイプです。

— C++ [basic.fundamental] 3.9.1/5

このnotesでは、wchar_tがすべてのロケールの文字を同時に表すのに十分な大きさである必要があります。つまり、wchar_tに使用されるエンコーディングはロケールによって異なる場合があります。つまり、あるロケールを使用して文字列をwchar_tに変換した後、別のロケールを使用してcharに戻す必要はありません。1

Wchar_tをすべてのロケール間で共通の表現として使用することが、実際にはwchar_tの主な用途であると思われるので、そうでない場合は何が良いのか疑問に思うかもしれません。

Wchar_tの本来の意図と目的は、文字列のコード単位からテキストの文字への1対1のマッピングを必要とするように定義することにより、テキスト処理を簡単にし、使用されるのと同じ単純なアルゴリズムを使用できるようにすることでしたASCII文字列を使用して、他の言語で動作します。

残念ながら、wchar_tの仕様の文言は、これを実現するために文字とコードポイント間の1対1のマッピングを想定しています。 Unicodeはその仮定を破ります2、したがって、単純なテキストアルゴリズムにもwchar_tを安全に使用することはできません。

つまり、ポータブルソフトウェアでは、ロケール間のテキストの一般的な表現として、または単純なテキストアルゴリズムの使用を可能にするために、wchar_tを使用できません。

今日のwchar_tの用途は何ですか?

とにかく、移植可能なコードのために。 __STDC_ISO_10646__が定義されている場合、wchar_tの値は、すべてのロケールで同じ値を持つUnicodeコードポイントを直接表します。これにより、前述のロケール間変換を安全に実行できます。ただし、ほとんどのUNIXプラットフォームでは定義されていますが、Windowsはすべてのロケールで同じwchar_tロケールを使用しているにもかかわらず、この方法でwchar_tを使用できると判断することだけに頼ることはできません。

Windowsが__STDC_ISO_10646__を定義しない理由は、Windowsがそのwchar_tエンコーディングとしてUTF-16を使用し、UTF-16がサロゲートペアを使用してU + FFFFより大きいコードポイントを表すためです。つまり、UTF-16は__STDC_ISO_10646__の要件を満たします。

プラットフォーム固有のコードの場合、wchar_tの方が便利です。 Windowsでは基本的に必要です(たとえば、一部のファイルはwchar_tファイル名を使用しないと開くことができません)。ただし、私の知る限りWindowsがこれに該当する唯一のプラットフォームです(したがって、wchar_tを「Windows_char_t」と考えることができます)。

後知恵では、wchar_tは、テキスト処理を単純化するために、またはロケールに依存しないテキストのストレージとして明らかに有用ではありません。移植可能なコードは、これらの目的のために使用しようとしないでください。移植性のないコードは、一部のAPIがそれを必要とするという理由だけで便利だと思うかもしれません。

代替案

私が好む代替案は、UTF-8に特に優しいプラットフォームではなくても、UTF-8でエンコードされたC文字列を使用することです。

このように、プラットフォーム間で共通のテキスト表現を使用して移植可能なコードを記述し、意図した目的に標準データ型を使用し、それらの型の言語サポートを取得できます(たとえば、一部のコンパイラーで動作させるためにいくつかのトリックが必要ですが)標準ライブラリのサポート、デバッガーのサポート(より多くのトリックが必要な場合があります)など。ワイド文字を使用すると、これらすべてを取得することは一般的に困難または不可能であり、プラットフォームごとに異なるピースを取得できます。

UTF-8が提供しないことの1つは、ASCIIで可能なような単純なテキストアルゴリズムを使用する機能です。このUTF-8は、他のUnicodeエンコーディングよりも悪くありません。実際、UTF-8のマルチコードユニット表現がより一般的であるため、より良いと見なされる可能性があります。そのため、文字のこのような可変幅表現を処理するコードのバグは、UTFに固執しようとする場合よりも気づき、修正される可能性が高くなります-32 with NFCまたはNFKC。

多くのプラットフォームはネイティブの文字エンコーディングとしてUTF-8を使用しており、多くのプログラムは重要なテキスト処理を必要としないため、これらのプラットフォームでの国際化プログラムの作成は、国際化を考慮しないコードの作成とほとんど変わりません。より広く移植可能なコードを作成するか、他のプラットフォームで作成するには、他のエンコーディングを使用するAPIの境界に変換を挿入する必要があります。

一部のソフトウェアで使用される別の代替方法は、UTF-16データを保持する符号なしの短い配列などのクロスプラットフォーム表現を選択し、すべてのライブラリサポートを提供し、言語サポートなどのコストをそのまま負担することです。

C++ 11は、付随する言語/ライブラリ機能を備えたwchar_t、char16_t、およびchar32_tの代替として、新しい種類のワイド文字を追加します。これらは実際にはUTF-16およびUTF-32であるとは保証されていませんが、主要な実装で他のものが使用されるとは思いません。 C++ 11は、UTF-8文字列リテラルなどを使用してUTF-8サポートも改善しているため、VC++をtoしてUTF-8エンコード文字列を生成する必要はありません(ただし、u8プレフィックス)。

避けるべき代替案

TCHAR:TCHARは、レガシーエンコーディングをcharからwchar_tに想定する古代のWindowsプログラムを移行するためのものであり、プログラムが以前の千年紀で書かれていない限り、最も忘れられがちです。移植性がなく、エンコードやデータ型についても本質的に不特定であるため、非TCHARベースのAPIでは使用できません。その目的は上記で見たwchar_tへの移行であり、これは良い考えではないため、TCHARを使用することには何の価値もありません。


1. wchar_t文字列で表現できるが、どのロケールでもサポートされていない文字は、単一のwchar_t値で表現する必要はありません。これは、wchar_tが特定の文字に可変幅エンコードを使用する可能性があることを意味します。これは、wchar_tの意図に対する別の明らかな違反です。 wchar_tで表現できる文字は、ロケールがその文字を「サポート」していると言うのに十分であると主張できますが、その場合、可変幅エンコーディングは有効ではなく、WindowのUTF-16の使用は非準拠です。

2. Unicodeを使用すると、多くの文字を複数のコードポイントで表すことができます。これにより、単純なテキストアルゴリズムでも可変幅エンコーディングと同じ問題が発生します。構成された正規化を厳密に維持しても、一部の文字には複数のコードポイントが必要です。参照: http://www.unicode.org/standard/where/

112
bames53

Wchar_tには「間違った」ものは何もありません。問題は、NT 3.xの時代に、MicrosoftはUnicodeが良かったと判断し、Unicodeを16ビットのwchar_t文字として実装することでした。したがって、90年代半ば以降のほとんどのMicrosoftの文献は、Unicode == utf16 == wchar_tとほぼ同一視されていました。

悲しいことに、これはまったくそうではありません。 「ワイド文字」は、notであり、すべてのプラットフォームで、すべての状況下で必然的に2バイトです。

これは私が今まで見た「Unicode」(C++に依存せず、この質問に依存しない)に関する最良の入門書の1つです。Ihighly

そして、「8ビットASCII」対「Win32ワイド文字」対「wchar_t-in-general」に対処する最善の方法は、「Windows is Different」を受け入れ、それに応じてコーディングすることであると正直に信じています。

私見では...

PS:

私は上記のjamesdlinに完全に同意します:

Windowsでは、実際に選択肢はありません。その内部APIはUCS-2向けに設計されました。これは、可変長UTF-8およびUTF-16エンコードが標準化される前だったため、当時合理的でした。しかし、UTF-16をサポートするようになったため、両方の世界で最悪の結果になりました。

18
paulsm4