Meine Plattform ist ein Mac und C ++ 11 (oder höher). Ich bin ein C ++ - Anfänger und arbeite an einem persönlichen Projekt, das Chinesisch und Englisch verarbeitet. UTF-8 ist die bevorzugte Codierung für dieses Projekt.
Ich habe einige Beiträge zu Stack Overflow gelesen, und viele von ihnen schlagen vor, sie std::string
beim Umgang mit UTF-8 zu verwenden und zu vermeiden, wchar_t
da es derzeit keine char8_t
für UTF-8 gibt.
Doch keiner von ihnen darüber sprechen , wie man richtig mit Funktionen umgehen wie str[i]
, std::string::size()
, std::string::find_first_of()
oder , std::regex
da diese Funktion gibt in der Regel unerwartete Ergebnisse , wenn UTF-8 gegenüber .
Soll ich weitermachen std::string
oder zu wechseln std::wstring
? Wenn ich bei bleiben sollte std::string
, was ist die beste Vorgehensweise, um die oben genannten Probleme zu lösen?
std::wstring
?! ) Würden Sie mit UTF-8 verwenden?std::string::size()
ist nur dann überraschend, wenn Sie erwarten, dass Sie etwas anderes tun, als die Länge in Bytes zurückzugeben, dh Codeeinheiten (nicht die Anzahl der Codepunkte in der Zeichenfolge). Undstr[i]
gibt das i-te Byte in der Zeichenfolge zurück. Dies wäre jedoch auch dann der Fall, wenn C ++ einenchar8_t
Typ speziell für UTF-8 hätte.swift unicode
undswift regex
alles ist für Sie erledigt.Antworten:
Unicode-Glossar
Unicode ist ein umfangreiches und komplexes Thema. Ich möchte dort nicht zu tief waten, aber ein kurzes Glossar ist notwendig:
Dies ist die Basis von Unicode. Die Unterscheidung zwischen Codepunkt und Graphemcluster kann größtenteils beschönigt werden, da für die meisten modernen Sprachen jedes "Zeichen" einem einzelnen Codepunkt zugeordnet ist (es gibt spezielle akzentuierte Formen für häufig verwendete Buchstaben- / diakritische Kombinationen). Wenn Sie sich jedoch in Smileys, Flaggen usw. wagen, müssen Sie möglicherweise auf die Unterscheidung achten.
UTF-Grundierung
Dann muss eine Reihe von Unicode-Codepunkten codiert werden. Die gemeinsamen Codierungen sind UTF-8, UTF-16 und UTF-32, wobei die beiden letzteren sowohl in Little-Endian- als auch in Big-Endian-Form existieren, für insgesamt 5 gemeinsame Codierungen.
In UTF-X, X die Größe in Bits der Code Einheit , jeder Code Point als eine oder mehrere Kode Units in Abhängigkeit von ihrer Größe dargestellt wird:
std::string
undstd::wstring
.std::wstring
wenn Sie Wert auf Portabilität legen (wchar_t
unter Windows sind es nur 16 Bit). benutzestd::u32string
stattdessen (akastd::basic_string<char32_t>
).std::string
oderstd::wstring
) ist unabhängig von der Darstellung auf der Festplatte (UTF-8, UTF-16 oder UTF-32). Bereiten Sie sich also darauf vor, dass Sie an der Grenze konvertieren müssen (Lesen und Schreiben).wchar_t
sicherstellt, dass eine Codeeinheit einen vollständigen Codepunkt darstellt, repräsentiert sie immer noch keinen vollständigen Graphemcluster.Wenn Sie nur Zeichenfolgen lesen oder komponieren, sollten Sie keine oder nur geringe Probleme mit
std::string
oder habenstd::wstring
.Probleme beginnen, wenn Sie mit dem Schneiden und Würfeln beginnen. Dann müssen Sie auf (1) Codepunktgrenzen (in UTF-8 oder UTF-16) und (2) Graphemclustergrenzen achten. Ersteres kann problemlos selbst gehandhabt werden, letzteres erfordert die Verwendung einer Unicode-fähigen Bibliothek.
Pflücken
std::string
oderstd::u32string
?Wenn die Leistung ein Problem darstellt, ist es wahrscheinlich, dass
std::string
die Leistung aufgrund des geringeren Speicherbedarfs besser ist. obwohl starker Gebrauch von Chinesisch das Geschäft ändern kann. Wie immer Profil.Wenn Graphemcluster kein Problem darstellen,
std::u32string
hat dies den Vorteil, dass die Dinge vereinfacht werden: 1 Codeeinheit -> 1 Codepunkt bedeutet, dass Sie Codepunkte nicht versehentlich aufteilen können und alle Funktionen sofort einsatzbereit sindstd::basic_string
.Wenn Sie mit Software verbunden sind
std::string
oderchar*
/char const*
, halten Sie sich anstd::string
, um hin und her Konvertierungen zu vermeiden. Sonst wird es ein Schmerz sein.UTF-8 in
std::string
.UTF-8 funktioniert eigentlich ganz gut in
std::string
.Die meisten Vorgänge funktionieren sofort, da die UTF-8-Codierung selbstsynchronisiert und abwärtskompatibel mit ASCII ist.
Aufgrund der Art und Weise, wie Codepunkte codiert werden, kann die Suche nach einem Codepunkt nicht versehentlich mit der Mitte eines anderen Codepunkts übereinstimmen:
str.find('\n')
funktioniert,str.find("...")
funktioniert für den Abgleich von Byte für Byte 1 ,str.find_first_of("\r\n")
funktioniert bei der Suche nach ASCII-Zeichen .Ebenso
regex
sollte meistens sofort funktionieren. Da eine Folge von Zeichen ("haha"
) nur eine Folge von Bytes ("哈"
) ist, sollten grundlegende Suchmuster sofort funktionieren.Seien Sie jedoch vorsichtig bei Zeichenklassen (z. B.
[:alphanum:]
), da diese je nach Regex-Geschmack und Implementierung möglicherweise mit Unicode-Zeichen übereinstimmen oder nicht.Seien Sie in ähnlicher Weise vorsichtig, wenn Sie Repeater auf Nicht-ASCII- "Zeichen" anwenden, und betrachten Sie
"哈?"
möglicherweise nur das letzte Byte als optional. Verwenden Sie Klammern, um die wiederholte Folge von Bytes in solchen Fällen klar abzugrenzen :"(哈)?"
.1 Die Schlüsselkonzepte für die Suche sind Normalisierung und Sortierung. Dies betrifft alle Vergleichsoperationen.
std::string
vergleicht (und sortiert) immer Byte für Byte, ohne Rücksicht auf sprach- oder verwendungsspezifische Vergleichsregeln. Wenn Sie die vollständige Normalisierung / Sortierung durchführen möchten, benötigen Sie eine vollständige Unicode-Bibliothek, z. B. die Intensivstation.quelle
str.find_first_of
,str.find
oderstd::regex
scheint nicht Arbeit für nicht ASCII - Eingänge (zB „哈“ oder u8 „哈“) gegebenstd::string str(u8"哈哈haha");
str.find("哈")
sollte funktionieren (siehe ideone.com/s9i1yf ), wird aberstr.find('哈')
nicht, weil'哈'
es sich um ein Multi-Byte-Zeichen handelt.str.find_first_of("哈")
funktioniert nicht (funktioniert nur für ASCII-Muster). Regex sollte für ASCII-Muster gut funktionieren. Achten Sie jedoch auf Zeichenklassen und "Repeater" (z. B."哈?"
darf nur das letzte Byte bedingt sein).std::basic_string<char32_t>
sowohl unter * nix als auch unter Windows wie erwartet funktionieren?std::u32string
.str.find("...")str.fin works
Nur wenn Sie sich nur darum kümmern, Byte für Byte abzugleichen - andernfalls benötigen Sie einen ordnungsgemäßen Vergleich, der die Normalisierung und das Gebietsschema berücksichtigt. Davon abgesehen scheint dies eine ziemlich gute Antwort zu sein und zeigt, warum ich die Unicode- "Unterstützung", die in Sprachen wie Python3 existiert, irgendwie hasse.std::string
und Freunde sind kodierungsunabhängig. Der einzige Unterschied zwischenstd::wstring
undstd::string
sind , dassstd::wstring
Verwendungenwchar_t
als Einzelelement, nichtchar
. Für die meisten Compiler ist letzteres 8-Bit. Ersteres soll groß genug sein, um Unicode-Zeichen aufzunehmen, in der Praxis jedoch auf einigen Systemen nicht (der Microsoft-Compiler verwendet beispielsweise einen 16-Bit-Typ). Sie können UTF-8 nicht in speichernstd::wstring
. Dafür ist es nicht gedacht. Es entspricht UTF-32 - einer Zeichenfolge, bei der jedes Element ein einzelner Unicode-Codepunkt ist.Wenn Sie UTF-8-Zeichenfolgen nach Unicode-Codepunkt oder zusammengesetztem Unicode-Glyphen (oder einem anderen Element) indizieren möchten, zählen Sie die Länge einer UTF-8-Zeichenfolge in Unicode-Codepunkten oder einem anderen Unicode-Objekt oder suchen Sie nach Unicode-Codepunkt Sie müssen etwas anderes als die Standardbibliothek verwenden. Die Intensivstation ist eine der Bibliotheken auf diesem Gebiet. es kann andere geben.
Bemerkenswert ist wahrscheinlich, dass Sie bei der Suche nach ASCII-Zeichen einen UTF-8-Bytestream meistens so behandeln können, als wäre er Byte für Byte. Jedes ASCII-Zeichen codiert in UTF-8 dasselbe wie in ASCII, und jede Multi-Byte-Einheit in UTF-8 enthält garantiert keine Bytes im ASCII-Bereich.
quelle
std::wstring
eigentlich nicht um eine Multi-Byte-Codierung handeln sollte. das ist der Punkt des Typs. Das Erstellen einer Multi-Byte-Codierung (und einer schlechten) ist nur ein Duplizierenstd::string
, aber auf eine wirklich nervige Weise, die die Leute dazu bringt, zu glauben, dass ihr Code Unicode richtig macht.Beide
std::string
undstd::wstring
müssen UTF-Codierung verwenden, um Unicode darzustellen. Unter macOSstd::string
ist UTF-8 (8-Bit-Codeeinheiten) undstd::wstring
UTF-32 (32-Bit-Codeeinheiten). Beachten Sie, dass die Größe vonwchar_t
plattformabhängig ist.Verfolgt für beide
size
die Anzahl der Codeeinheiten anstelle der Anzahl der Codepunkte oder Graphemcluster. (Ein Codepunkt ist eine Unicode-Entität, von der einer oder mehrere einen Graphemcluster bilden. Graphemcluster sind die sichtbaren Zeichen, mit denen Benutzer interagieren, z. B. Buchstaben oder Emojis.)Obwohl ich mit der Unicode-Darstellung von Chinesisch nicht vertraut bin, ist es sehr wahrscheinlich, dass bei Verwendung von UTF-32 die Anzahl der Codeeinheiten häufig sehr nahe an der Anzahl der Graphemcluster liegt. Dies geht natürlich zu Lasten des bis zu vierfachen Speicherbedarfs.
Die genaueste Lösung wäre die Verwendung einer Unicode-Bibliothek wie z. B. der Intensivstation, um die gewünschten Unicode-Eigenschaften zu berechnen.
Schließlich eignen sich UTF-Zeichenfolgen in menschlichen Sprachen, in denen keine Zeichen kombiniert werden, normalerweise recht gut mit
find
/regex
. Ich bin mir nicht sicher über Chinesisch, aber Englisch ist einer von ihnen.quelle
std::string str(u8"哈哈haha");str.find_first_of(u8"haha");
scheint zu funktionieren, geben Siestr.find_first_of(u8"哈ha");
immer 0 zurück. Und Regex scheint auch nicht zu funktionieren.find_first_of
mitfind
.find_first_of
kann nicht mit Multi-Byte-Zeichen arbeiten.size
die Anzahl der Codepunkte " - falsch, es repräsentiert Codeeinheiten , keine Codepunkte . Großer Unterschied. " anstelle der Anzahl der logischen Zeichen. (Logische Zeichen sind ein oder mehrere Codepunkte.) " - auch formal als Graphemcluster bekannt.std::string
in UTF8 sein, auch wenn wir zu neigen UTF8 überall . Ich vermute, dass ein EBCDIC-Mainframe EBCDIC fürstd::string
std::string
"verwendet" keine Codierung, weder UTF-8 noch EBCDIC.std::string
ist nur ein Container für Bytes von Typenchar
. Sie können dort UTF-8-Zeichenfolgen oder ASCII-Zeichenfolgen oder EBCDIC-Zeichenfolgen oder sogar Binärdaten einfügen. Die Codierung dieser Bytes (falls vorhanden) wird vom Rest Ihres Programms bestimmt und davon, was Sie mit der Zeichenfolge tun, nicht vonstd::string
selbst.Erwägen Sie ein Upgrade auf C ++ 20, und
std::u8string
das ist das Beste, was wir ab 2019 für das Halten von UTF-8 haben. Es gibt keine Standardbibliotheksfunktionen für den Zugriff auf einzelne Codepunkte oder Graphemcluster, aber zumindest ist Ihr Typ stark genug, um zumindest zu sagen, dass es sich um echtes UTF-8 handelt.quelle