Was ist "falsch" mit C ++ wchar_t und wstrings? Was sind einige Alternativen zu breiten Zeichen?

86

Ich habe viele Leute in der C ++ - Community gesehen (insbesondere ## c ++ auf freenode), die die Verwendung von wstringsund wchar_tund ihre Verwendung in der Windows-API ablehnen. Was ist genau "falsch" an wchar_tund wstring, und wenn ich die Internationalisierung unterstützen möchte, welche Alternativen gibt es zu breiten Charakteren?

Ken Li
quelle
1
Haben Sie Referenzen dafür?
Dani
14
Vielleicht beantwortet dieser großartige Thread alle Ihre Fragen? stackoverflow.com/questions/402283/stdwstring-vs-stdstring
MrFox
15
Unter Windows haben Sie keine Wahl. Die internen APIs wurden für UCS-2 entwickelt, was zu dieser Zeit angemessen war, da die UTF-8- und UTF-16-Codierungen mit variabler Länge standardisiert wurden. Aber jetzt, wo sie UTF-16 unterstützen, haben sie das Schlimmste aus beiden Welten.
Jamesdlin
12
utf8everywhere.org hat eine gute Diskussion der Gründe, um breite Zeichen zu vermeiden.
JoeG
5
@ Jamesdlin Sicher hast du eine Wahl. Die nowide-Bibliothek bietet eine bequeme Möglichkeit, Zeichenfolgen nur beim Übergeben an die APIs zu konvertieren. API-Aufrufe mit Zeichenfolgen sind normalerweise niederfrequent. Daher ist es sinnvoll, ad-hok zu konvertieren und ständig Dateien und interne Variablen in UTF-8 zu haben.
Pavel Radzivilovsky

Antworten:

114

Was ist wchar_t?

wchar_t ist so definiert, dass die Zeichencodierung eines Gebietsschemas in eine wchar_t-Darstellung konvertiert werden kann, wobei jedes wchar_t genau einen Codepunkt darstellt:

Der Typ wchar_t ist ein eindeutiger Typ, dessen Werte unterschiedliche Codes für alle Mitglieder des größten erweiterten Zeichensatzes darstellen können, der in den unterstützten Gebietsschemas angegeben ist (22.3.1).

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

Dies erfordert nicht , dass wchar_t groß genug ist, um ein Zeichen aus allen Gebietsschemas gleichzeitig darzustellen. Das heißt, die für wchar_t verwendete Codierung kann zwischen den Gebietsschemas unterschiedlich sein. Dies bedeutet, dass Sie einen String nicht unbedingt mit einem Gebietsschema in wchar_t konvertieren und dann mit einem anderen Gebietsschema wieder in char konvertieren können. 1

Da die Verwendung von wchar_t als gemeinsame Darstellung aller Gebietsschemas in der Praxis die Hauptverwendung für wchar_t zu sein scheint, fragen Sie sich möglicherweise, wozu es gut ist, wenn nicht.

Die ursprüngliche Absicht und der ursprüngliche Zweck von wchar_t bestand darin, die Textverarbeitung zu vereinfachen, indem sie so definiert wurde, dass eine Eins-zu-Eins-Zuordnung von den Codeeinheiten einer Zeichenfolge zu den Zeichen des Textes erforderlich ist, wodurch die Verwendung der gleichen einfachen Algorithmen ermöglicht wird, die verwendet werden mit ASCII-Zeichenfolgen, um mit anderen Sprachen zu arbeiten.

Leider setzt der Wortlaut der Spezifikation von wchar_t eine Eins-zu-Eins-Zuordnung zwischen Zeichen und Codepunkten voraus, um dies zu erreichen. Unicode bricht diese Annahme 2 , sodass Sie wchar_t auch für einfache Textalgorithmen nicht sicher verwenden können.

Dies bedeutet, dass tragbare Software wchar_t weder als allgemeine Darstellung für Text zwischen Gebietsschemas noch zur Verwendung einfacher Textalgorithmen verwenden kann.

Was nützt wchar_t heute?

Nicht viel, für tragbaren Code sowieso. Wenn __STDC_ISO_10646__definiert, repräsentieren die Werte von wchar_t direkt Unicode-Codepunkte mit denselben Werten in allen Gebietsschemas. Das macht es sicher, die zuvor erwähnten Konvertierungen zwischen den Ländereinstellungen durchzuführen. Sie können sich jedoch nicht nur darauf verlassen, dass Sie wchar_t auf diese Weise verwenden können, da Windows dies zwar von den meisten Unix-Plattformen definiert, Windows jedoch nicht, obwohl Windows in allen Gebietsschemas dasselbe Gebietsschema wchar_t verwendet.

Der Grund, warum Windows nicht definiert, __STDC_ISO_10646__liegt darin, dass Windows UTF-16 als wchar_t-Codierung verwendet und dass UTF-16 Ersatzpaare verwendet, um Codepunkte darzustellen, die größer als U + FFFF sind, was bedeutet, dass UTF-16 die Anforderungen für nicht erfüllt __STDC_ISO_10646__.

Für plattformspezifischen Code kann wchar_t nützlicher sein. Es ist im Wesentlichen unter Windows erforderlich (z. B. können einige Dateien ohne Verwendung von wchar_t-Dateinamen einfach nicht geöffnet werden), obwohl Windows meines Wissens die einzige Plattform ist, auf der dies zutrifft (also können wir uns wchar_t vielleicht als 'Windows_char_t' vorstellen).

Im Nachhinein ist wchar_t eindeutig nicht nützlich, um die Textverarbeitung zu vereinfachen oder als Speicherort für länderunabhängigen Text. Portable Code sollte nicht versuchen, ihn für diese Zwecke zu verwenden. Nicht portabler Code kann ihn einfach deshalb nützlich finden, weil einige APIs ihn erfordern.

Alternativen

Die Alternative, die ich mag, ist die Verwendung von UTF-8-codierten C-Strings, selbst auf Plattformen, die für UTF-8 nicht besonders geeignet sind.

Auf diese Weise kann man portablen Code mithilfe einer gemeinsamen plattformübergreifenden Textdarstellung schreiben, Standarddatentypen für den beabsichtigten Zweck verwenden und die Unterstützung der Sprache für diese Typen erhalten (z. B. Zeichenfolgenliterale, obwohl einige Tricks erforderlich sind, damit sie für einige Compiler funktionieren), andere Standard-Bibliotheksunterstützung, Debugger-Unterstützung (möglicherweise sind weitere Tricks erforderlich) usw. Bei breiten Zeichen ist es im Allgemeinen schwieriger oder unmöglich, all dies zu erhalten, und Sie erhalten möglicherweise verschiedene Teile auf verschiedenen Plattformen.

Eine Sache, die UTF-8 nicht bietet, ist die Möglichkeit, einfache Textalgorithmen zu verwenden, wie sie mit ASCII möglich sind. In diesem UTF-8 ist nicht schlechter als jede andere Unicode-Codierung. Tatsächlich kann dies als besser angesehen werden, da Darstellungen von Einheiten mit mehreren Codeeinheiten in UTF-8 häufiger vorkommen und daher Fehler bei der Codebehandlung solcher Darstellungen von Zeichen mit variabler Breite eher bemerkt und behoben werden, als wenn Sie versuchen, sich an UTF zu halten -32 mit NFC oder NFKC.

Viele Plattformen verwenden UTF-8 als native Zeichencodierung, und viele Programme erfordern keine nennenswerte Textverarbeitung. Daher unterscheidet sich das Schreiben eines internationalisierten Programms auf diesen Plattformen kaum vom Schreiben von Code ohne Berücksichtigung der Internationalisierung. Das Schreiben von allgemein portablem Code oder das Schreiben auf anderen Plattformen erfordert das Einfügen von Konvertierungen an den Grenzen von APIs, die andere Codierungen verwenden.

Eine andere Alternative, die von einigen Softwareprogrammen verwendet wird, besteht darin, eine plattformübergreifende Darstellung auszuwählen, z. B. vorzeichenlose kurze Arrays mit UTF-16-Daten, und dann die gesamte Bibliotheksunterstützung bereitzustellen und einfach mit den Kosten für die Sprachunterstützung usw. zu leben.

C ++ 11 fügt neue Arten von breiten Zeichen als Alternativen zu wchar_t, char16_t und char32_t mit zugehörigen Sprach- / Bibliotheksfunktionen hinzu. Es wird nicht garantiert, dass dies UTF-16 und UTF-32 sind, aber ich kann mir nicht vorstellen, dass eine größere Implementierung etwas anderes verwenden wird. C ++ 11 verbessert auch die UTF-8-Unterstützung, beispielsweise mit UTF-8-Zeichenfolgenliteralen, sodass VC ++ nicht dazu verleitet werden muss, UTF-8-codierte Zeichenfolgen zu erstellen (obwohl ich dies möglicherweise weiterhin tun werde, anstatt das u8Präfix zu verwenden). .

Alternativen zu vermeiden

TCHAR: TCHAR dient zur Migration alter Windows-Programme, die Legacy-Codierungen von char nach wchar_t annehmen, und wird am besten vergessen, es sei denn, Ihr Programm wurde in einem früheren Jahrtausend geschrieben. Es ist nicht portierbar und von Natur aus unspezifisch in Bezug auf seine Codierung und sogar seinen Datentyp, wodurch es mit jeder nicht TCHAR-basierten API unbrauchbar wird. Da der Zweck die Migration nach wchar_t ist, was wir oben gesehen haben, ist dies keine gute Idee. Die Verwendung von TCHAR hat keinerlei Wert.


1. Zeichen, die in wchar_t-Zeichenfolgen darstellbar sind, aber in keinem Gebietsschema unterstützt werden, müssen nicht mit einem einzigen wchar_t-Wert dargestellt werden. Dies bedeutet, dass wchar_t für bestimmte Zeichen eine Codierung mit variabler Breite verwenden kann, eine weitere eindeutige Verletzung der Absicht von wchar_t. Obwohl es fraglich ist, ob ein durch wchar_t darstellbares Zeichen ausreicht, um zu sagen, dass das Gebietsschema dieses Zeichen "unterstützt", sind in diesem Fall Codierungen mit variabler Breite nicht zulässig und die Verwendung von UTF-16 durch Window ist nicht konform.

2. Mit Unicode können viele Zeichen mit mehreren Codepunkten dargestellt werden, was bei einfachen Textalgorithmen dieselben Probleme verursacht wie bei Codierungen mit variabler Breite. Selbst wenn man eine zusammengesetzte Normalisierung strikt beibehält, erfordern einige Zeichen immer noch mehrere Codepunkte. Siehe: http://www.unicode.org/standard/where/

bames53
quelle
3
Ergänzung: utf8everywhere.org empfiehlt die Verwendung von UTF-8 unter Windows, und Boost.Nowide soll offiziell überprüft werden.
Yakov Galka
2
Das Beste ist natürlich, C # oder VB.Net unter Windows zu verwenden :) Oder einfach altes C / Win32. Wenn Sie jedoch C ++ verwenden müssen, ist TCHAR der beste Weg. Der Standardwert ist "wchar_t" bei MSVS2005 und höher. IMHO ...
paulsm4
4
@BrendanMcK: Sicher, Code, der die Win32-API unter Windows und andere APIs auf anderen Systemen verwendet, existiert nicht. Richtig? Das Problem mit Microsofts Ansatz ( „Verwendung wchar intern überall in der App“) ist , dass selbst Code wirkt sich das nicht direkt auf das System hat eine Schnittstelle und kann tragbar sein.
Yakov Galka
4
Das Problem ist , dass Sie haben Windows-spezifische Funktionen , weil Microsofts Entscheidung , nicht zu verwenden , um Unterstützung von UTF-8 als ANSI - Codepage „Pausen“ der Standard C (++) Bibliothek. Beispielsweise können Sie keine fopenDatei erstellen, deren Name Nicht-ANSI-Zeichen enthält.
dan04
11
@ dan04 Ja, Sie können die Standardbibliothek unter Windows nicht verwenden, aber Sie können eine tragbare Schnittstelle erstellen, die die Standardbibliothek auf anderen Plattformen umschließt und direkt vor der Verwendung der Win32 W-Funktionen von UTF-8 in wchar_t konvertiert.
Bames53
20

Mit wchar_t ist nichts "falsch". Das Problem ist, dass Microsoft in NT 3.x-Tagen entschieden hat, dass Unicode gut ist (es ist) und Unicode als 16-Bit-Zeichen wchar_t implementiert. Die meiste Microsoft-Literatur aus der Mitte der 90er Jahre entsprach also ziemlich genau Unicode == utf16 == wchar_t.

Was leider überhaupt nicht der Fall ist. "Breite Zeichen" sind unter allen Umständen nicht unbedingt 2 Byte auf allen Plattformen.

Dies ist eines der besten Primer auf „Unicode“ (unabhängig von dieser Frage unabhängig von C ++) ich je gesehen habe: ich sehr es empfehlen:

Und ich glaube ehrlich, dass der beste Weg, mit "8-Bit-ASCII" gegen "Win32-Wide-Zeichen" gegen "wchar_t-in-general" umzugehen, einfach darin besteht, zu akzeptieren, dass "Windows anders ist" ... und entsprechend zu codieren.

MEINER BESCHEIDENEN MEINUNG NACH...

PS:

Ich stimme jamesdlin oben voll und ganz zu:

Unter Windows haben Sie keine Wahl. Die internen APIs wurden für UCS-2 entwickelt, was zu der Zeit sinnvoll war, da die UTF-8- und UTF-16-Codierungen mit variabler Länge standardisiert wurden. Aber jetzt, wo sie UTF-16 unterstützen, haben sie das Schlimmste aus beiden Welten.

paulsm4
quelle