std :: wstring VS std :: string

741

Ich kann die Unterschiede zwischen std::stringund nicht verstehen std::wstring. Ich weiß, dass wstringbreite Zeichen wie Unicode-Zeichen unterstützt werden. Ich habe folgende Fragen:

  1. Wann sollte ich std::wstringüber verwenden std::string?
  2. Kann std::stringden gesamten ASCII-Zeichensatz einschließlich der Sonderzeichen enthalten?
  3. Wird std::wstringvon allen gängigen C ++ - Compilern unterstützt?
  4. Was genau ist ein " breiter Charakter "?
Rapptz
quelle
10
Das ASCII-Charachter-Set enthält nicht viele "Sonderzeichen", das exotischste ist wahrscheinlich "(backquote)". std :: string kann ungefähr 0,025% aller Unicode-Zeichen enthalten (normalerweise 8-Bit-Zeichen)
MSalters
3
Gute Informationen über breite Zeichen und den zu verwendenden Typ finden Sie hier: programmers.stackexchange.com/questions/102205/…
Yariv
14
Nun, und seit wir 2012 sind, wurde utf8everywhere.org geschrieben. Es beantwortet so ziemlich alle Fragen zu Rechten und Fehlern mit C ++ / Windows.
Pavel Radzivilovsky
42
@MSalters: std :: string kann 100% aller Unicode-Zeichen enthalten, auch wenn CHAR_BIT 8 ist. Dies hängt von der Codierung von std :: string ab, die auf Systemebene UTF-8 sein kann (wie fast überall außer in Windows) ) oder auf Ihrer Anwendungsebene. Native Narrow-Codierung unterstützt Unicode nicht? Kein Problem, verwenden Sie es einfach nicht, sondern verwenden Sie stattdessen UTF-8.
Yakov Galka
8
Tolle Lektüre zu diesem Thema: utf8everywhere.org
Timothy Shields

Antworten:

992

string? wstring?

std::stringist eine basic_stringVorlage auf a charund std::wstringauf a wchar_t.

char vs. wchar_t

charsoll ein Zeichen enthalten, normalerweise ein 8-Bit-Zeichen.
wchar_tsoll ein breites Zeichen enthalten, und dann wird es schwierig:
Unter Linux sind a wchar_t4 Bytes, unter Windows 2 Bytes.

Was ist dann mit Unicode ?

Das Problem ist, dass weder charnoch wchar_tdirekt an Unicode gebunden ist.

Unter Linux?

Nehmen wir ein Linux-Betriebssystem: Mein Ubuntu-System ist bereits Unicode-fähig. Wenn ich mit einer Zeichenfolge arbeite, wird diese nativ in UTF-8 (dh einer Unicode-Zeichenfolge) codiert . Der folgende Code:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

gibt folgenden Text aus:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

Sie werden sehen, dass der "olé" -Text in charwirklich aus vier Zeichen besteht: 110, 108, 195 und 169 (ohne die nachfolgende Null). (Ich lasse Sie den wchar_tCode als Übung studieren )

Wenn charSie unter Linux arbeiten, sollten Sie normalerweise Unicode verwenden, ohne es zu wissen. Und wie es std::stringfunktioniert char, std::stringist es bereits Unicode-fähig.

Beachten Sie, dass std::stringdie "olé" -String wie die C-String-API 4 Zeichen und nicht drei Zeichen enthält. Sie sollten daher beim Abschneiden / Spielen mit Unicode-Zeichen vorsichtig sein, da eine Kombination von Zeichen in UTF-8 verboten ist.

Unter Windows?

Unter Windows ist dies etwas anders. Win32 musste vor dem Aufkommen von Unicode viele Anwendungen unterstützen, die mit charund an verschiedenen Zeichensätzen / Codepages arbeiten, die auf der ganzen Welt produziert wurden.

Ihre Lösung war also interessant: Wenn eine Anwendung mit funktioniert char, werden die Zeichenfolgen mithilfe des lokalen Zeichensatzes / der Codepage auf dem Computer auf GUI-Etiketten codiert / gedruckt / angezeigt. Zum Beispiel wäre "olé" in einem französisch lokalisierten Windows "olé", in einem kyrillisch lokalisierten Windows jedoch etwas anderes ("olй", wenn Sie Windows-1251 verwenden ). Daher funktionieren "historische Apps" normalerweise immer noch auf die gleiche Weise.

Für Unicode-basierte Anwendungen verwendet Windows eine wchar_t2-Byte-Breite und ist in UTF-16 codiert , das in Unicode-Codierung mit 2-Byte-Zeichen (oder zumindest dem meist kompatiblen UCS-2) codiert ist das Gleiche IIRC).

Anwendungen, die verwendet charwerden, werden als "Multibyte" bezeichnet (da jede Glyphe aus einem oder mehreren chars besteht), während Anwendungen, die verwendet wchar_twerden, als "widechar" bezeichnet werden (weil jede Glyphe aus einem oder zwei besteht wchar_t. Weitere Informationen finden Sie unter MultiByteToWideChar und WideCharToMultiByte Win32-Konvertierungs-API.

Wenn Sie also unter Windows arbeiten, möchten Sie es unbedingt verwenden wchar_t(es sei denn, Sie verwenden ein Framework, das dies verbirgt, wie GTK + oder QT ...). Tatsache ist, dass Windows hinter den Kulissen mit wchar_tZeichenfolgen arbeitet, sodass selbst historische Anwendungen ihre charZeichenfolgen wchar_tbei Verwendung von API-ähnlichen Konvertierungen konvertieren können SetWindowText()(Low-Level-API-Funktion zum Festlegen der Bezeichnung auf einer Win32-GUI).

Speicherprobleme?

UTF-32 besteht aus 4 Bytes pro Zeichen, sodass nicht viel hinzugefügt werden muss, wenn nur ein UTF-8-Text und ein UTF-16-Text immer weniger oder die gleiche Speichermenge benötigen wie ein UTF-32-Text (und normalerweise weniger) ).

Wenn es ein Speicherproblem gibt, sollten Sie wissen, dass UTF-8-Text für die meisten westlichen Sprachen weniger Speicher benötigt als derselbe UTF-16-Text.

Für andere Sprachen (Chinesisch, Japanisch usw.) ist der verwendete Speicher für UTF-8 entweder gleich oder etwas größer als für UTF-16.

Alles in allem verwendet UTF-16 meistens 2 und gelegentlich 4 Bytes pro Zeichen (es sei denn, Sie haben es mit einer Art esoterischer Sprachglyphen (klingonisch? Elbisch?) Zu tun, während UTF-8 1 bis 4 Bytes verbraucht.

Weitere Informationen finden Sie unter http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 .

Fazit

  1. Wann sollte ich std :: wstring über std :: string verwenden?

    Unter Linux? Fast nie (§).
    Unter Windows? Fast immer (§).
    Auf plattformübergreifendem Code? Kommt auf dein Toolkit an ...

    (§): sofern Sie kein Toolkit / Framework verwenden, das etwas anderes sagt

  2. Kann std::stringden gesamten ASCII-Zeichensatz einschließlich Sonderzeichen enthalten?

    Hinweis: A std::stringeignet sich zum Halten eines 'binären' Puffers, a std::wstringnicht!

    Unter Linux? Ja.
    Unter Windows? Für das aktuelle Gebietsschema des Windows-Benutzers sind nur Sonderzeichen verfügbar.

    Bearbeiten (nach einem Kommentar von Johann Gerell ):
    a std::stringreicht aus, um alle charZeichenfolgen zu verarbeiten (jede charist eine Zahl von 0 bis 255). Aber:

    1. ASCII soll von 0 auf 127 gehen. Höhere chars sind NICHT ASCII.
    2. a charvon 0 bis 127 wird korrekt gehalten
    3. a charvon 128 bis 255 hat abhängig von Ihrer Codierung (Unicode, Nicht-Unicode usw.) eine Bedeutung, kann jedoch alle Unicode-Glyphen enthalten, solange sie in UTF-8 codiert sind.
  3. Wird std::wstringvon fast allen gängigen C ++ - Compilern unterstützt?

    Meistens mit Ausnahme von GCC-basierten Compilern, die auf Windows portiert sind.
    Es funktioniert unter meinem g ++ 4.3.2 (unter Linux) und ich habe die Unicode-API unter Win32 seit Visual C ++ 6 verwendet.

  4. Was genau ist ein breiter Charakter?

    In C / C ++ ist ein Zeichentyp geschrieben, wchar_tder größer als der einfache charZeichentyp ist. Es soll verwendet werden, um Zeichen einzufügen, deren Indizes (wie Unicode-Glyphen) größer als 255 sind (oder 127, abhängig von ...).

paercebal
quelle
4
@gnud: Vielleicht sollte wchar_t ausreichen, um alle UCS-2-Zeichen (die meisten UTF-16-Zeichen) vor dem Aufkommen von UTF-16 zu verarbeiten ... Oder Microsoft hatte andere Prioritäten als POSIX, wie den einfachen Zugriff auf Unicode ohne die codepaged Verwendung von char unter Win32 zu ändern.
Paercebal
4
@ Sorin Sbarnea: UTF-8 könnte 1-6 Bytes benötigen, aber anscheinend beschränkt der Standard es auf 1-4. Weitere Informationen finden Sie unter en.wikipedia.org/wiki/UTF8#Description .
Paercebal
8
Während dieses Beispiel unter Linux und Windows unterschiedliche Ergebnisse liefert, enthält das C ++ - Programm ein implementierungsdefiniertes Verhalten, ob olèes als UTF-8 codiert ist oder nicht. Der Grund, warum Sie nicht nativ streamen können wchar_t *, std::coutist, dass die Typen nicht kompatibel sind, was zu einem schlecht geformten Programm führt und nichts mit der Verwendung von Codierungen zu tun hat. Es ist erwähnenswert, dass Sie Ihre Codierungspräferenz anstelle der Plattform verwenden std::stringoder von dieser std::wstringabhängen, insbesondere wenn Sie möchten, dass Ihr Code portabel ist.
John Leidegren
14
Windows verwendet tatsächlich UTF-16 und seit einiger Zeit verwenden ältere Windows-Versionen UCS-2, dies ist jedoch nicht mehr der Fall. Mein einziges Problem hier ist die Schlussfolgerung, std::wstringdie unter Windows verwendet werden sollte, da sie besser zu der Unicode-Windows-API passt, die ich für trügerisch halte. Wenn Ihr einziges Anliegen darin bestand, die Unicode-Windows-API aufzurufen und keine Zeichenfolgen zu marshallen, dann sicher, aber ich kaufe dies nicht als allgemeinen Fall.
John Leidegren
15
@ John Leidegren :: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureDann sind wir uns einig. Ich codiere in C ++, nicht in JavaScript. Das Vermeiden von nutzlosem Marshalling oder anderen potenziell kostspieligen Verarbeitungen zur Laufzeit, wenn diese zur Kompilierungszeit ausgeführt werden können, ist das Herzstück dieser Sprache. Das Codieren gegen WinAPI und dessen Verwendung std::stringist nur eine ungerechtfertigte Verschwendung von Laufzeitressourcen. Sie finden es trügerisch und es ist in Ordnung, da es Ihr Standpunkt ist. Meine eigene ist, dass ich unter Windows keinen Code mit Pessimierung schreibe, nur weil er von Linux aus besser aussieht.
Paercebal
71

Ich empfehle, std::wstringunter Windows oder anderswo zu vermeiden , es sei denn, dies wird von der Benutzeroberfläche benötigt oder in der Nähe von Windows-API-Aufrufen und entsprechenden Codierungskonvertierungen als syntaktischer Zucker.

Meine Ansicht ist in http://utf8everywhere.org zusammengefasst, dessen Mitautor ich bin.

Sofern Ihre Anwendung nicht API-aufrufzentriert ist, z. B. hauptsächlich UI-Anwendungen, wird empfohlen, Unicode-Zeichenfolgen in std :: string zu speichern und in UTF-8 zu codieren, um eine Konvertierung in der Nähe von API-Aufrufen durchzuführen. Die im Artikel beschriebenen Vorteile überwiegen den offensichtlichen Ärger bei der Konvertierung, insbesondere bei komplexen Anwendungen. Dies gilt in zweifacher Hinsicht für die Entwicklung mehrerer Plattformen und Bibliotheken.

Und jetzt beantworten Sie Ihre Fragen:

  1. Ein paar schwache Gründe. Es existiert aus historischen Gründen, wo Widechars als die richtige Art der Unterstützung von Unicode angesehen wurden. Es wird jetzt verwendet, um APIs zu verbinden, die UTF-16-Zeichenfolgen bevorzugen. Ich benutze sie nur in unmittelbarer Nähe solcher API-Aufrufe.
  2. Dies hat nichts mit std :: string zu tun. Es kann jede Codierung enthalten, die Sie eingegeben haben. Die Frage ist nur, wie Sie mit dem Inhalt umgehen. Meine Empfehlung ist UTF-8, damit alle Unicode-Zeichen korrekt gespeichert werden können. Unter Linux ist dies eine gängige Praxis, aber ich denke, Windows-Programme sollten dies auch tun.
  3. Nein.
  4. Breites Zeichen ist ein verwirrender Name. In den frühen Tagen von Unicode gab es die Überzeugung, dass ein Zeichen in zwei Bytes codiert werden kann, daher der Name. Heute steht es für "jeden Teil des Zeichens, der zwei Bytes lang ist". UTF-16 wird als eine Folge solcher Bytepaare (auch bekannt als breite Zeichen) angesehen. Ein Zeichen in UTF-16 benötigt entweder ein oder zwei Paare.
Pavel Radzivilovsky
quelle
37

Jeder Leser hier sollte jetzt ein klares Verständnis für die Fakten und die Situation haben. Wenn nicht, müssen Sie die außerordentlich umfassende Antwort von paercebal lesen [übrigens: danke!].

Meine pragmatische Schlussfolgerung ist schockierend einfach: Alles, was mit C ++ (und STL) "Zeichencodierung" zu tun hat, ist im Wesentlichen kaputt und nutzlos. Schuld daran ist Microsoft oder nicht, das wird sowieso nicht helfen.

Meine Lösung nach eingehender Untersuchung, viel Frustration und den daraus resultierenden Erfahrungen ist folgende:

  1. Akzeptieren Sie, dass Sie selbst für die Codierung und Konvertierung verantwortlich sein müssen (und Sie werden sehen, dass vieles davon eher trivial ist).

  2. Verwenden Sie std :: string für alle UTF-8-codierten Zeichenfolgen (nur a typedef std::string UTF8String).

  3. Akzeptieren Sie, dass ein solches UTF8String-Objekt nur ein dummer, aber billiger Container ist. Greifen Sie niemals direkt auf darin enthaltene Zeichen zu und / oder manipulieren Sie diese (kein Suchen, Ersetzen usw.). Sie könnten, aber Sie möchten wirklich nur wirklich, wirklich nicht Ihre Zeit damit verschwenden, Textmanipulationsalgorithmen für Mehrbyte-Zeichenfolgen zu schreiben! Auch wenn andere Leute schon so dumme Sachen gemacht haben, tu das nicht! Kümmer dich nicht darum! (Nun, es gibt Szenarien, in denen es sinnvoll ist ... verwenden Sie einfach die ICU-Bibliothek für diese).

  4. Verwenden Sie std :: wstring für UCS-2-codierte Zeichenfolgen ( typedef std::wstring UCS2String) - dies ist ein Kompromiss und ein Zugeständnis an das Durcheinander, das die WIN32-API eingeführt hat. UCS-2 ist für die meisten von uns ausreichend (dazu später mehr ...).

  5. Verwenden Sie UCS2String-Instanzen, wenn ein zeichenweiser Zugriff erforderlich ist (Lesen, Bearbeiten usw.). Jede zeichenbasierte Verarbeitung sollte in einer NON-Multibyte-Darstellung erfolgen. Es ist einfach, schnell, einfach.

  6. Fügen Sie zwei Dienstprogrammfunktionen hinzu, um zwischen UTF-8 und UCS-2 hin und her zu konvertieren:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    

Die Konvertierungen sind unkompliziert, Google sollte hier helfen ...

Das ist es. Verwenden Sie UTF8String überall dort, wo Speicherplatz wertvoll ist, und für alle UTF-8-E / A. Verwenden Sie UCS2String überall dort, wo die Zeichenfolge analysiert und / oder bearbeitet werden muss. Sie können jederzeit zwischen diesen beiden Darstellungen konvertieren.

Alternativen & Verbesserungen

  • Konvertierungen von & in Einzelbyte-Zeichencodierungen (z. B. ISO-8859-1) können mit Hilfe von einfachen Übersetzungstabellen, z. B. const wchar_t tt_iso88951[256] = {0,1,2,...};und geeignetem Code für die Konvertierung nach & von UCS2, durchgeführt werden.

  • Wenn UCS-2 nicht ausreicht, wechseln Sie zu UCS-4 ( typedef std::basic_string<uint32_t> UCS2String)

Intensivstation oder andere Unicode-Bibliotheken?

Für Fortgeschrittene.

Frunsi
quelle
Verdammt, es ist nicht gut zu wissen, dass native Unicode-Unterstützung nicht vorhanden ist.
Mihai Danila
@Frunsi, ich bin gespannt, ob du Glib :: ustring ausprobiert hast und wenn ja, was denkst du?
Caroline Beltran
@CarolineBeltran: Ich kenne Glib, aber ich habe es nie benutzt, und ich werde es wahrscheinlich nie benutzen, weil es eher auf eine eher unspezifische Zielplattform beschränkt ist (unixoide Systeme ...). Sein Windows-Port basiert auf der externen win2unix-Schicht, und dort gibt es meiner Meinung nach überhaupt keine OSX-Kompatibilitätsschicht. All dieses Zeug weist eindeutig in eine falsche Richtung, zumindest für meinen Code (auf dieser Arch-Ebene ...) ;-) Also, Glib ist keine Option
Frunsi
9
Suchen, Ersetzen usw. funktioniert problemlos mit UTF-8-Zeichenfolgen (ein Teil der Byte-Sequenz, die ein Zeichen darstellt, kann niemals als anderes Zeichen falsch interpretiert werden). Tatsächlich machen UTF-16 und UTF-32 dies überhaupt nicht einfacher: Alle drei Codierungen sind in der Praxis Multibyte-Codierungen, da ein vom Benutzer wahrgenommenes Zeichen (Graphemcluster) eine beliebige Anzahl von Unicode-Codepunkten lang sein kann! Die pragmatische Lösung besteht darin, UTF-8 für alles zu verwenden und nur dann mit UTF-16 zu konvertieren, wenn Sie mit der Windows-API arbeiten.
Daniel
5
@Frunsi: Suchen und Ersetzen funktioniert mit UTF-8 genauso gut wie mit UTF-32. Gerade weil eine ordnungsgemäße Unicode-fähige Textverarbeitung ohnehin mit Multi-Codepoint-Zeichen umgehen muss, wird die Verwendung einer Zeichenfolgenverarbeitung mit variabler Länge wie UTF-8 nicht komplizierter. Verwenden Sie also einfach überall UTF-8. Normale C-Zeichenfolgenfunktionen funktionieren unter UTF-8 einwandfrei (und entsprechen Ordnungsvergleichen für die Unicode-Zeichenfolge). Wenn Sie mehr Sprachkenntnisse benötigen, müssen Sie auf jeden Fall eine Unicode-Bibliothek aufrufen, UTF-16/32 kann dich nicht davor retten.
Daniel
25
  1. Wenn Sie breite Zeichen in Ihrer Zeichenfolge speichern möchten. widehängt von der Implementierung ab. Visual C ++ ist standardmäßig 16 Bit, wenn ich mich richtig erinnere, während GCC je nach Ziel standardmäßig verwendet wird. Es ist hier 32 Bit lang. Bitte beachten Sie, dass wchar_t (breiter Zeichentyp) nichts mit Unicode zu tun hat. Es wird lediglich garantiert, dass alle Mitglieder des größten Zeichensatzes gespeichert werden können, den die Implementierung von ihren Gebietsschemas unterstützt, und mindestens so lange wie char. Sie können Unicode-Zeichenfolgen auch in der Codierung speichern . Die Bedeutung von Unicode-Codepunkten wird jedoch nicht verstanden. Damitstd::stringutf-8str.size()Sie erhalten nicht die Anzahl der logischen Zeichen in Ihrer Zeichenfolge, sondern lediglich die Anzahl der in dieser Zeichenfolge / Zeichenfolge gespeicherten char- oder wchar_t-Elemente. Aus diesem Grund haben die Leute des gtk / glib C ++ - Wrappers eine Glib::ustringKlasse entwickelt, die mit utf-8 umgehen kann.

    Wenn Ihr wchar_t 32 Bit lang ist, können Sie es utf-32als Unicode-Codierung verwenden und Unicode-Zeichenfolgen mit einer festen Codierung (utf-32 ist feste Länge) speichern und verarbeiten. Dies bedeutet, dass die s.size()Funktion Ihrer Zeichenfolge dann die richtige Anzahl von wchar_t-Elementen und logischen Zeichen zurückgibt .

  2. Ja, char ist immer mindestens 8 Bit lang, was bedeutet, dass alle ASCII-Werte gespeichert werden können.
  3. Ja, alle großen Compiler unterstützen dies.
Johannes Schaub - litb
quelle
Ich bin neugierig auf # 2. Ich dachte 7 Bits wären auch technisch gültig? Oder muss etwas gespeichert werden können, das über 7-Bit-ASCII-Zeichen hinausgeht?
Jalf
1
Ja, Jalf. c89 gibt in der Dokumentation von limit.h (für vorzeichenloses Zeichen 0..255 min) minimale Bereiche für Basistypen und ein reines Binärsystem für ganzzahlige Typen an. Es folgt char, unsigned char und signed char haben eine minimale Bitlänge von 8. c ++ erbt diese Regeln.
Johannes Schaub - litb
15
"Dies bedeutet, dass die Funktion s.size () Ihres Wstrings dann die richtige Anzahl von wchar_t-Elementen und logischen Zeichen zurückgibt." Dies ist selbst für Unicode nicht ganz korrekt. Es wäre genauer, Codepunkt als "logisches Zeichen" zu sagen, selbst in UTF-32 kann ein bestimmtes Zeichen aus mehreren Codepunkten bestehen.
Logan Capaldo
Sagt ihr im Wesentlichen, dass C ++ keine native Unterstützung für den Unicode-Zeichensatz hat?
Mihai Danila
1
"Aber es wird die Bedeutung von Unicode-Codepunkten nicht verstehen." Unter Windows auch nicht std::wstring.
Deduplikator
5

Ich benutze häufig std :: string, um utf-8-Zeichen ohne Probleme zu halten. Ich empfehle dies von Herzen, wenn Sie eine Schnittstelle zu APIs herstellen, die utf-8 ebenfalls als nativen Zeichenfolgentyp verwenden.

Zum Beispiel verwende ich utf-8, wenn ich meinen Code mit dem Tcl-Interpreter verbinde.

Die größte Einschränkung ist die Länge der Zeichenfolge std :: string, nicht mehr die Anzahl der Zeichen in der Zeichenfolge.


quelle
1
Juan: Meinst du, dass std :: string alle Unicode-Zeichen enthalten kann, aber die Länge falsch angibt? Gibt es einen Grund dafür, dass eine falsche Länge gemeldet wird?
3
Bei Verwendung der utf-8-Codierung kann ein einzelnes Unicode-Zeichen aus mehreren Bytes bestehen. Aus diesem Grund ist die utf-8-Codierung kleiner, wenn hauptsächlich Zeichen aus dem Standard-ASCII-Satz verwendet werden. Sie müssen spezielle Funktionen verwenden (oder Ihre eigenen rollen), um die Anzahl der Unicode-Zeichen zu messen.
2
(Windows-spezifisch) Die meisten Funktionen erwarten, dass eine Zeichenfolge, die Bytes verwendet, ASCII und 2 Bytes Unicode ist, ältere Versionen MBCS. Wenn Sie also 8-Bit-Unicode speichern, müssen Sie in 16-Bit-Unicode konvertieren, um eine Standard-Windows-Funktion aufzurufen (es sei denn, Sie verwenden nur den ASCII-Teil).
Greg Domjan
2
Ein std :: string meldet nicht nur die Länge falsch, sondern gibt auch den falschen String aus. Wenn ein Unicode-Zeichen in UTF-8 als mehrere Bytes dargestellt wird, die std :: string als seine eigenen Zeichen betrachtet, geben Ihre typischen Manipulationsroutinen für std :: string wahrscheinlich die mehreren seltsamen Zeichen aus, die sich aus der Fehlinterpretation des einen ergeben korrekter Charakter.
Mihai Danila
2
Ich schlage vor, die Antwort zu ändern, um anzuzeigen, dass Zeichenfolgen nur als Container mit Bytes betrachtet werden sollten. Wenn es sich bei den Bytes um eine Unicode-Codierung handelt (UTF-8, UTF-16, ...), sollten Sie bestimmte Bibliotheken verwenden, die dies verstehen Das. Die auf Standardzeichenfolgen basierenden APIs (Länge, Substrat usw.) schlagen alle mit Multibyte-Zeichen kläglich fehl. Wenn dieses Update durchgeführt wird, werde ich meine Downvote entfernen.
Mihai Danila
4
  1. Wenn Sie 'breite' (Unicode) Zeichen speichern möchten.
  2. Ja: 255 davon (ohne 0).
  3. Ja.
  4. Hier ist ein Einführungsartikel: http://www.joelonsoftware.com/articles/Unicode.html
ChrisW
quelle
11
std :: string kann 0 gut halten (seien Sie vorsichtig, wenn Sie die Methode c_str () aufrufen)
Mr Fooz
3
Und genau genommen ist ein Zeichen nicht garantiert 8 Bit. :) Dein Link in # 4 ist ein Muss, aber ich denke nicht, dass er die Frage beantwortet. Ein breites Zeichen hat streng genommen nichts mit Unicode zu tun. Es ist einfach ein breiterer Charakter. (Wie viel breiter hängt vom Betriebssystem ab, aber normalerweise 16 oder 32 Bit)
Jalf
2
  1. Wenn Sie Unicode-Zeichenfolgen und nicht nur ASCII verwenden möchten, ist dies hilfreich für die Internationalisierung
  2. Ja, aber es spielt nicht gut mit 0
  3. Ich weiß nichts davon
  4. Wide Character ist die compilerspezifische Methode zur Behandlung der Darstellung eines Unicode-Zeichens mit fester Länge. Für MSVC ist es ein 2-Byte-Zeichen, für gcc sind es meines Wissens 4 Byte. und +1 für http://www.joelonsoftware.com/articles/Unicode.html
Greg Domjan
quelle
1
2. Ein std :: string kann ein NULL-Zeichen enthalten. Es kann auch utf-8 und breite Zeichen enthalten.
@ Juan: Das hat mich wieder verwirrt. Wenn std :: string Unicode-Zeichen behalten kann, was ist das Besondere an std :: wstring?
1
@Appu: std :: string kann UTF-8-Unicode-Zeichen enthalten. Es gibt eine Reihe von Unicode-Standards, die auf unterschiedliche Zeichenbreiten abzielen. UTf8 ist 8 Bit breit. Es gibt auch UTF-16 und UTF-32 mit 16 bzw. 32 Bit Breite
Greg D
Mit einem std :: wstring. Jedes Unicode-Zeichen kann bei Verwendung der Codierungen mit fester Länge ein wchar_t sein. Wenn Sie beispielsweise den Joel-on-Software-Ansatz verwenden, auf den Greg verweist. Dann ist die Länge der Zeichenfolge genau die Anzahl der Unicode-Zeichen in der Zeichenfolge. Aber es nimmt mehr Platz ein
Ich habe nicht gesagt, dass es keine 0 '\ 0' halten kann, und was ich damit gemeint habe, ist, dass einige Methoden möglicherweise nicht das erwartete Ergebnis liefern, das alle Daten der Zeichenfolge enthält. So hart bei den Abstimmungen.
Greg Domjan
2

Anwendungen, die nicht mit nur 256 verschiedenen Zeichen zufrieden sind, können entweder breite Zeichen (mehr als 8 Bit) oder eine Codierung variabler Länge (eine Multibyte-Codierung in der C ++ - Terminologie) wie UTF-8 verwenden. Breite Zeichen benötigen im Allgemeinen mehr Speicherplatz als eine Codierung mit variabler Länge, sind jedoch schneller zu verarbeiten. Mehrsprachige Anwendungen, die große Textmengen verarbeiten, verwenden bei der Verarbeitung des Texts normalerweise breite Zeichen, konvertieren ihn jedoch beim Speichern auf der Festplatte in UTF-8.

Der einzige Unterschied zwischen a stringund a wstringist der Datentyp der Zeichen, die sie speichern. In einer Zeichenfolge werden chars gespeichert, deren Größe garantiert mindestens 8 Bit beträgt, sodass Sie Zeichenfolgen für die Verarbeitung von z. B. ASCII-, ISO-8859-15- oder UTF-8-Text verwenden können. Der Standard sagt nichts über den Zeichensatz oder die Codierung aus.

Praktisch jeder Compiler verwendet einen Zeichensatz, dessen erste 128 Zeichen ASCII entsprechen. Dies ist auch bei Compilern der Fall, die UTF-8-Codierung verwenden. Bei der Verwendung von Zeichenfolgen in UTF-8 oder einer anderen Codierung mit variabler Länge ist zu beachten, dass die Indizes und Längen in Bytes und nicht in Zeichen gemessen werden.

Der Datentyp eines Wstrings ist wchar_t, dessen Größe im Standard nicht definiert ist, außer dass er mindestens so groß wie ein Zeichen sein muss, normalerweise 16 Bit oder 32 Bit. wstring kann zur Verarbeitung von Text in der implementierungsdefinierten Breitzeichencodierung verwendet werden. Da die Codierung im Standard nicht definiert ist, ist die Konvertierung zwischen Zeichenfolgen und Zeichenfolgen nicht einfach. Man kann auch nicht davon ausgehen, dass wstrings eine Codierung mit fester Länge haben.

Wenn Sie keine mehrsprachige Unterstützung benötigen, können Sie möglicherweise nur reguläre Zeichenfolgen verwenden. Wenn Sie dagegen eine grafische Anwendung schreiben, unterstützt die API häufig nur breite Zeichen. Dann möchten Sie wahrscheinlich die gleichen breiten Zeichen verwenden, wenn Sie den Text verarbeiten. Beachten Sie, dass UTF-16 eine Codierung mit variabler Länge ist, was bedeutet, dass Sie nicht davon ausgehen können length(), die Anzahl der Zeichen zurückzugeben. Wenn die API eine Codierung mit fester Länge wie UCS-2 verwendet, wird die Verarbeitung einfach. Das Konvertieren zwischen breiten Zeichen und UTF-8 ist auf tragbare Weise schwierig, aber andererseits unterstützt Ihre Benutzeroberflächen-API wahrscheinlich die Konvertierung.

Seppo Enarvi
quelle
Um den ersten Absatz zu paraphrasieren: Anwendungen, die mehr als 256 Zeichen benötigen, müssen eine Multibyte-Codierung oder eine Vielleicht_Multibyte-Codierung verwenden.
Deduplikator
Im Allgemeinen werden 16- und 32-Bit-Codierungen wie UCS-2 und UCS-4 jedoch nicht als Multibyte-Codierungen bezeichnet. Der C ++ - Standard unterscheidet zwischen Multibyte-Codierungen und breiten Zeichen. Eine breite Zeichendarstellung verwendet eine feste Anzahl (im Allgemeinen mehr als 8) Bits pro Zeichen. Codierungen, die ein einzelnes Byte zum Codieren der häufigsten Zeichen und mehrere Bytes zum Codieren des restlichen Zeichensatzes verwenden, werden als Multibyte-Codierungen bezeichnet.
Seppo Enarvi
Entschuldigung, schlampiger Kommentar. Sollte die Codierung mit variabler Länge haben. UTF-16 ist genau wie UTF-8 eine Codierung mit variabler Länge. Es ist keine schlechte Idee, so zu tun, als wäre es nicht so .
Deduplikator
Das ist ein guter Punkt. Es gibt keinen Grund, warum wstrings nicht zum Speichern von UTF-16 (anstelle von UCS-2) verwendet werden könnten, aber dann geht die Bequemlichkeit einer Codierung mit fester Länge verloren.
Seppo Enarvi
2

Eine gute Frage! Ich denke, dass DATA ENCODING (manchmal auch ein CHARSET ) ein MEMORY EXPRESSION MECHANISM ist, um Daten in einer Datei zu speichern oder Daten über ein Netzwerk zu übertragen. Daher beantworte ich diese Frage wie folgt:

1. Wann sollte ich std :: wstring über std :: string verwenden?

Wenn es sich bei der Programmierplattform oder API-Funktion um eine Einzelbyte-Funktion handelt und wir einige Unicode-Daten verarbeiten oder analysieren möchten, z. B. aus der Windows'.REG-Datei oder dem 2-Byte-Netzwerk-Stream lesen, sollten wir die Variable std :: wstring als einfach deklarieren verarbeite sie. Beispiel: wstring ws = L "中国 a" (6-Oktett-Speicher: 0x4E2D 0x56FD 0x0061), wir können ws [0] verwenden, um das Zeichen '中' und ws [1] zu erhalten, um das Zeichen '国' und ws [2] zu erhalten Holen Sie sich das Zeichen 'a' usw.

2. Kann std :: string den gesamten ASCII-Zeichensatz einschließlich der Sonderzeichen enthalten?

Ja. Beachten Sie jedoch: Amerikanisches ASCII bedeutet, dass jedes 0x00 ~ 0xFF-Oktett für ein Zeichen steht, einschließlich druckbaren Textes wie "123abc & * _ &", und Sie sagten einen speziellen, meistens als '.' Vermeiden Sie verwirrende Editoren oder Terminals. Und einige andere Länder erweitern ihren eigenen "ASCII" -Zeichensatz, z. B. Chinesisch. Verwenden Sie 2 Oktette, um für ein Zeichen zu stehen.

3. Wird std :: wstring von allen gängigen C ++ - Compilern unterstützt?

Vielleicht oder meistens. Ich habe verwendet: VC ++ 6 und GCC 3.3, JA

4. Was ist genau ein "breiter Charakter"?

Ein breites Zeichen bedeutet meistens, dass 2 Oktette oder 4 Oktette verwendet werden, um die Zeichen aller Länder zu speichern. 2 Oktett UCS2 ist ein repräsentatives Beispiel, und außerdem, z. B. Englisch 'a', ist sein Speicher 2 Oktett von 0x0061 (im Gegensatz zu ASCII 'a ist der Speicher 1 Oktett 0x61).

Leiyi.China
quelle
0

Hier gibt es einige sehr gute Antworten, aber ich denke, ich kann einige Dinge in Bezug auf Windows / Visual Studio hinzufügen. Dies basiert auf meinen Erfahrungen mit VS2015. Unter Linux besteht die Antwort im Grunde darin, std::stringüberall UTF-8 zu codieren . Unter Windows / VS wird es komplexer. Hier ist warum. Windows erwartet, dass mit chars gespeicherte Zeichenfolgen mithilfe der Codepage des Gebietsschemas codiert werden. Dies ist fast immer der ASCII-Zeichensatz, gefolgt von 128 weiteren Sonderzeichen, abhängig von Ihrem Standort. Lassen Sie mich nur feststellen, dass dies nicht nur bei Verwendung der Windows-API der Fall ist, sondern dass es drei weitere wichtige Stellen gibt, an denen diese Zeichenfolgen mit Standard-C ++ interagieren. Hierbei handelt es sich um Zeichenfolgenliterale, die ausgegeben werden, um einen Dateinamen zu std::coutverwenden <<und an diesen zu übergeben std::fstream.

Ich werde hier ganz vorne mit dabei sein, dass ich Programmierer und kein Sprachspezialist bin. Ich schätze, dass USC2 und UTF-16 nicht dasselbe sind, aber für meine Zwecke sind sie nahe genug, um austauschbar zu sein, und ich verwende sie hier als solche. Ich bin mir nicht sicher, welches Windows verwendet, aber ich muss es im Allgemeinen auch nicht wissen. Ich habe UCS2 in dieser Antwort angegeben. Es tut mir also im Voraus leid, wenn ich jemanden mit meiner Unkenntnis dieser Angelegenheit verärgert habe, und ich bin froh, sie zu ändern, wenn ich etwas falsch mache.

String-Literale

Wenn Sie Zeichenfolgenliterale eingeben, die nur Zeichen enthalten, die von Ihrer Codepage dargestellt werden können, speichert VS diese in Ihrer Datei mit 1 Byte pro Zeichencodierung basierend auf Ihrer Codepage. Beachten Sie, dass, wenn Sie Ihre Codepage ändern oder Ihre Quelle einem anderen Entwickler mit einer anderen Codepage geben, ich denke (aber nicht getestet habe), dass der Charakter anders enden wird. Wenn Sie Ihren Code auf einem Computer mit einer anderen Codepage ausführen, bin ich mir nicht sicher, ob sich auch das Zeichen ändert.

Wenn Sie Zeichenfolgenliterale eingeben, die nicht durch Ihre Codepage dargestellt werden können, werden Sie von VS aufgefordert, die Datei als Unicode zu speichern. Die Datei wird dann als UTF-8 codiert. Dies bedeutet, dass alle Nicht-ASCII-Zeichen (einschließlich der Zeichen auf Ihrer Codepage) durch 2 oder mehr Bytes dargestellt werden. Dies bedeutet, wenn Sie Ihre Quelle an eine andere Person weitergeben, sieht die Quelle gleich aus. Bevor die Quelle jedoch an den Compiler übergeben wird, konvertiert VS den UTF-8-codierten Text in Codepage-codierten Text, und alle auf der Codepage fehlenden Zeichen werden durch ersetzt ?.

Die einzige Möglichkeit, die korrekte Darstellung eines Unicode-Zeichenfolgenliterals in VS zu gewährleisten, besteht darin, dem Zeichenfolgenliteral Lein breites Zeichenfolgenliteral vorangestellt zu machen. In diesem Fall konvertiert VS den UTF-8-codierten Text aus der Datei in UCS2. Sie müssen dieses String-Literal dann an einen std::wstringKonstruktor übergeben oder es in utf-8 konvertieren und in a einfügen std::string. Wenn Sie möchten, können Sie die Windows-API-Funktionen verwenden, um sie mithilfe Ihrer Codepage zu codieren, um sie in ein zu setzen. std::stringMöglicherweise haben Sie jedoch auch kein breites Zeichenfolgenliteral verwendet.

std :: cout

Bei der Ausgabe an die Konsole mit können <<Sie nur verwenden std::string, nicht std::wstringund der Text muss mit Ihrer Gebietsschema-Codepage codiert werden. Wenn Sie ein haben std::wstring, müssen Sie es mit einer der Windows-API-Funktionen konvertieren. Alle Zeichen, die nicht auf Ihrer Codepage enthalten sind, werden durch ersetzt ?(möglicherweise können Sie das Zeichen ändern, ich kann mich nicht erinnern).

std :: fstream Dateinamen

Das Windows-Betriebssystem verwendet UCS2 / UTF-16 für seine Dateinamen, sodass Sie unabhängig von Ihrer Codepage Dateien mit einem beliebigen Unicode-Zeichen haben können. Dies bedeutet jedoch, dass Sie verwenden müssen, um auf Dateien mit Zeichen zuzugreifen oder diese zu erstellen, die sich nicht auf Ihrer Codepage befinden std::wstring. Es geht nicht anders. Dies ist eine Microsoft-spezifische Erweiterung std::fstream, die auf anderen Systemen wahrscheinlich nicht kompiliert werden kann. Wenn Sie std :: string verwenden, können Sie nur Dateinamen verwenden, die nur Zeichen auf Ihrer Codepage enthalten.

Deine Optionen

Wenn Sie nur unter Linux arbeiten, sind Sie wahrscheinlich nicht so weit gekommen. Verwenden Sie UTF-8 einfach std::stringüberall.

Wenn Sie nur unter Windows arbeiten, verwenden Sie UCS2 einfach std::wstringüberall. Einige Puristen mögen sagen, dass sie UTF8 verwenden und dann bei Bedarf konvertieren, aber warum sollten sie sich um den Ärger kümmern?

Wenn Sie plattformübergreifend sind, ist es ein Chaos, ehrlich zu sein. Wenn Sie versuchen, UTF-8 unter Windows überall zu verwenden, müssen Sie mit Ihren Zeichenfolgenliteralen und der Ausgabe an die Konsole sehr vorsichtig sein. Sie können Ihre Saiten dort leicht beschädigen. Wenn Sie std::wstringunter Linux überall verwenden, haben Sie möglicherweise keinen Zugriff auf die breite Version von std::fstream, sodass Sie die Konvertierung durchführen müssen, aber es besteht kein Risiko einer Beschädigung. Ich persönlich halte dies für eine bessere Option. Viele würden nicht zustimmen, aber ich bin nicht allein - es ist der Weg, den wxWidgets zum Beispiel eingeschlagen hat.

Eine andere Möglichkeit könnte darin bestehen, unicodestringwie std::stringunter Linux und std::wstringWindows zu tippen und ein Makro namens UNI () zu haben, das unter Windows L und vor Linux nichts vorstellt, dann den Code

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

wäre auf jeder Plattform in Ordnung, denke ich.

Antworten

Also, um deine Fragen zu beantworten

1) Wenn Sie für Windows programmieren, dann die ganze Zeit, wenn plattformübergreifend, dann vielleicht die ganze Zeit, es sei denn, Sie möchten sich mit möglichen Korruptionsproblemen unter Windows befassen oder Code mit einer Plattform schreiben, die spezifisch ist #ifdefs, um die Unterschiede zu umgehen , wenn Sie nur verwenden Linux dann nie.

2) Ja. Außerdem können Sie es unter Linux auch für alle Unicodes verwenden. Unter Windows können Sie es nur für alle Unicodes verwenden, wenn Sie sich für die manuelle Codierung mit UTF-8 entscheiden. Die Windows-API- und Standard-C ++ - Klassen erwarten jedoch std::string, dass die Codierung mithilfe der Codepage des Gebietsschemas erfolgt. Dies umfasst alle ASCII-Zeichen sowie weitere 128 Zeichen, die sich je nach der von Ihrem Computer eingerichteten Codepage ändern.

3) Ich glaube schon, aber wenn nicht, dann ist es nur ein einfaches typedef eines 'std :: basic_string' mit wchar_tanstelle vonchar

4) Ein breites Zeichen ist ein Zeichentyp, der größer als der 1-Byte-Standardtyp ist char. Unter Windows sind es 2 Bytes, unter Linux 4 Bytes.

Phil Rosenberg
quelle
1
Zu "Bevor jedoch die Quelle an den Compiler übergeben wird, konvertiert VS den UTF-8-codierten Text in Codepage-codierten Text und alle auf der Codepage fehlenden Zeichen werden durch? Ersetzt." -> Ich glaube nicht, dass dies zutrifft, wenn der Compiler UTF-8-Codierung (Verwendung /utf-8) verwendet.
Roi Danton
Ich war mir dessen als Option nicht bewusst. Über diesen Link docs.microsoft.com/en-us/cpp/build/reference/… scheint es kein Kontrollkästchen zu geben, das in den Projekteigenschaften ausgewählt werden kann. Sie müssen es als zusätzliche Befehlszeilenoption hinzufügen. Gute Stelle!
Phil Rosenberg
-2

1) Wie von Greg erwähnt, ist wstring hilfreich für die Internationalisierung. Dann werden Sie Ihr Produkt in anderen Sprachen als Englisch veröffentlichen

4) Überprüfen Sie dies auf breite Zeichen http://en.wikipedia.org/wiki/Wide_character

Raghu
quelle
-6

Wann sollten Sie KEINE breiten Zeichen verwenden?

Wenn Sie Code vor dem Jahr 1990 schreiben.

Natürlich bin ich Flip, aber jetzt ist es wirklich das 21. Jahrhundert. 127 Zeichen sind längst nicht mehr ausreichend. Ja, Sie können UTF8 verwenden, aber warum sollten Sie sich mit den Kopfschmerzen beschäftigen?


quelle
16
@dave: Ich weiß nicht, welche Kopfschmerzen UTF-8 verursacht, die größer sind als die von Widechars (UTF-16). In UTF-16 haben Sie auch mehrstellige Zeichen.
Pavel Radzivilovsky
Das Problem ist, dass Sie wchar_t verwenden MÜSSEN, wenn Sie irgendwo anders als im englischsprachigen Land sind. Ganz zu schweigen davon, dass einige Alphabete viel mehr Zeichen enthalten, als Sie in ein Byte passen können. Wir waren dort unter DOS. Codepage Schizophrenie, nein, danke, nicht mehr ..
Swift - Friday Pie
1
@Swift Das Problem dabei wchar_tist, dass seine Größe und Bedeutung betriebssystemspezifisch sind. Es tauscht nur die alten Probleme gegen neue aus. Während eine charist ein charunabhängig von OS (auf ähnliche Plattformen, zumindest). Wir könnten also genauso gut UTF-8 verwenden, alles in Sequenzen von chars packen und beklagen, dass C ++ uns ohne Standardmethoden zum Messen, Indizieren, Finden usw. in solchen Sequenzen völlig alleine lässt.
underscore_d
1
@Swift Du scheinst es komplett rückwärts zu haben. wchar_tist ein Datentyp mit fester Breite, sodass ein Array von 10 wchar_timmer sizeof(wchar_t) * 10Plattformbytes belegt. Und UTF-16 ist eine Codierung mit variabler Breite, bei der Zeichen aus 1 oder 2 16-Bit-Codepunkten bestehen können (und s / 16/8 / g für UTF-8).
underscore_d
1
@SteveHollasch wchar_t Die Darstellung einer Zeichenfolge in Windows würde Zeichen größer als FFFF als spezielles Ersatzpaar codieren, andere würden nur ein wchar_t-Element verwenden. Daher ist diese Darstellung nicht mit der vom Gnu-Compiler erstellten Darstellung kompatibel (wobei alle Zeichen, die kleiner als FFFF sind, kein Wort vor sich haben). Was in wchar_t gespeichert ist, wird vom Programmierer und Compiler bestimmt, nicht durch eine Vereinbarung
Swift - Friday Pie