Beachten Sie, dass es nicht um "strikte Unicode-Programmierung" an sich geht, sondern um praktische Erfahrung.
In meinem Unternehmen haben wir eine Wrapper-Bibliothek um die IBM ICU-Bibliothek erstellt. Die Wrapper-Bibliothek verfügt über eine UTF-8-Schnittstelle und wird in UTF-16 konvertiert, wenn die Intensivstation aufgerufen werden muss. In unserem Fall haben wir uns nicht allzu viele Sorgen um Performance-Hits gemacht. Wenn die Leistung ein Problem war, haben wir auch UTF-16-Schnittstellen bereitgestellt (unter Verwendung unseres eigenen Datentyps).
Anwendungen können weitgehend unverändert bleiben (mit char), obwohl sie in einigen Fällen bestimmte Probleme berücksichtigen müssen. Beispielsweise verwenden wir anstelle von strncpy () einen Wrapper, der das Abschneiden von UTF-8-Sequenzen vermeidet. In unserem Fall ist dies ausreichend, aber man könnte auch Prüfungen zum Kombinieren von Zeichen in Betracht ziehen. Wir haben auch Wrapper zum Zählen der Anzahl von Codepunkten, der Anzahl von Graphemen usw.
Bei der Schnittstelle mit anderen Systemen müssen wir manchmal eine benutzerdefinierte Zeichenkomposition durchführen, sodass Sie dort möglicherweise etwas Flexibilität benötigen (abhängig von Ihrer Anwendung).
Wir verwenden wchar_t nicht. Durch die Verwendung der Intensivstation werden unerwartete Probleme bei der Portabilität vermieden (aber natürlich keine anderen unerwarteten Probleme :-).
strncpy
die richtige Verwendung mit UTF-8 absolut sicher ist.strcpy
(was in der Tat sicher mit UTF-8 zu verwenden ist). Benutzer verwenden diesstrncpy
wahrscheinlich, weil sie nicht wissen, ob der Zielpuffer groß genug ist. Daher möchten sie eine maximale Anzahl von zu kopierenden Bytes übergeben - was tatsächlich zu ungültigen UTF-8-Sequenzen führen kann.C99 oder früher
Der C-Standard (C99) sieht breite Zeichen und Mehrbyte-Zeichen vor. Da jedoch keine Garantie dafür besteht, was diese breiten Zeichen enthalten können, ist ihr Wert etwas begrenzt. Für eine bestimmte Implementierung bieten sie nützliche Unterstützung. Wenn Ihr Code jedoch zwischen Implementierungen wechseln kann, gibt es keine ausreichende Garantie dafür, dass sie nützlich sind.
Folglich ist der von Hans van Eck vorgeschlagene Ansatz (der darin besteht, einen Wrapper um die ICU - International Components for Unicode - Bibliothek zu schreiben) solide, IMO.
Die UTF-8-Codierung hat viele Vorteile. Wenn Sie nicht mit den Daten herumspielen (z. B. durch Abschneiden), kann sie von Funktionen kopiert werden, die die Feinheiten von UTF-8 nicht vollständig kennen Codierung. Dies ist bei kategorisch nicht der Fall
wchar_t
.Unicode in Full ist ein 21-Bit-Format. Das heißt, Unicode reserviert Codepunkte von U + 0000 bis U + 10FFFF.
Eines der nützlichen Dinge bei den Formaten UTF-8, UTF-16 und UTF-32 (wobei UTF für Unicode-Transformationsformat steht - siehe Unicode ) ist, dass Sie ohne Informationsverlust zwischen den drei Darstellungen konvertieren können. Jeder kann alles darstellen, was der andere darstellen kann. Sowohl UTF-8 als auch UTF-16 sind Multi-Byte-Formate.
UTF-8 ist bekanntermaßen ein Multi-Byte-Format mit einer sorgfältigen Struktur, die es ermöglicht, den Anfang von Zeichen in einer Zeichenfolge zuverlässig zu finden, beginnend an jedem Punkt in der Zeichenfolge. Bei Einzelbyte-Zeichen ist das High-Bit auf Null gesetzt. Bei Mehrbytezeichen beginnt das erste Zeichen mit einem der Bitmuster 110, 1110 oder 11110 (für 2-Byte-, 3-Byte- oder 4-Byte-Zeichen), wobei nachfolgende Bytes immer mit 10 beginnen. Die Fortsetzungszeichen befinden sich immer in der Bereich 0x80 .. 0xBF. Es gibt Regeln, nach denen UTF-8-Zeichen im minimal möglichen Format dargestellt werden müssen. Eine Konsequenz dieser Regeln ist, dass die Bytes 0xC0 und 0xC1 (auch 0xF5..0xFF) nicht in gültigen UTF-8-Daten erscheinen können.
Ursprünglich hoffte man, dass Unicode ein 16-Bit-Code-Set sein würde und alles in einen 16-Bit-Code-Raum passen würde. Leider ist die reale Welt komplexer und musste auf die aktuelle 21-Bit-Codierung erweitert werden.
UTF-16 ist somit ein Code mit einer Einheit (16-Bit-Wort), der für die 'Basic Multilingual Plane' festgelegt wurde, dh die Zeichen mit den Unicode-Codepunkten U + 0000 .. U + FFFF, verwendet jedoch zwei Einheiten (32 Bit) für Zeichen außerhalb dieses Bereichs. Daher muss Code, der mit der UTF-16-Codierung funktioniert, Codierungen mit variabler Breite verarbeiten können, genau wie UTF-8. Die Codes für die Zeichen mit zwei Einheiten werden als Ersatzzeichen bezeichnet.
UTF-32 kann natürlich jeden Unicode-Codepunkt in einer einzelnen Speichereinheit codieren. Es ist effizient für die Berechnung, aber nicht für die Speicherung.
Weitere Informationen finden Sie auf den Websites ICU und Unicode.
C11 und
<uchar.h>
Der C11-Standard hat die Regeln geändert, aber nicht alle Implementierungen haben die Änderungen bereits jetzt (Mitte 2017) eingeholt. Der C11-Standard fasst die Änderungen für die Unicode-Unterstützung wie folgt zusammen:
Was folgt, ist ein minimaler Überblick über die Funktionalität. Die Spezifikation enthält:
(Übersetzen der Querverweise:
<stddef.h>
definiertsize_t
,<wchar.h>
definiertmbstate_t
und<stdint.h>
definiertuint_least16_t
unduint_least32_t
.) Der<uchar.h>
Header definiert auch einen minimalen Satz von (neu startbaren) Konvertierungsfunktionen:Es gibt Regeln, nach denen Unicode-Zeichen in Bezeichnern verwendet werden können, die die Notationen
\unnnn
oder\U00nnnnnn
verwenden. Möglicherweise müssen Sie die Unterstützung für solche Zeichen in Bezeichnern aktiv aktivieren. Zum Beispiel muss GCC-fextended-identifiers
diese in Bezeichnern zulassen.Beachten Sie, dass macOS Sierra (10.12.5), um nur eine Plattform zu nennen, keine Unterstützung bietet
<uchar.h>
.quelle
wchar_t
und Freunde hier ein bisschen kurz. Diese Typen sind wichtig, damit die C-Bibliothek Text in jeder Codierung (einschließlich Nicht-Unicode-Codierungen) verarbeiten kann. Ohne die umfangreichen Zeichentypen und -funktionen würde die C-Bibliothek für jede unterstützte Codierung eine Reihe von Textverarbeitungsfunktionen erfordern : Stellen Sie sich vor, Sie hätten koi8len, koi8tok, koi8printf nur für KOI-8-codierten Text und utf8len, utf8tok, utf8printf für UTF-8 Text. Stattdessen haben wir das Glück nur zu haben , eine Reihe dieser Funktionen (nicht die Original - ASCII diejenigen mitgezählt):wcslen
,wcstok
, undwprintf
.mbstowcs
lediglich die Zeichenkonvertierungsfunktionen der C-Bibliothek ( und Freunde) verwenden, um die unterstützte Codierung in zu konvertierenwchar_t
. Nach demwchar_t
Formatieren kann der Programmierer die einzelnen Funktionen zur Verarbeitung von Breittexten verwenden, die die C-Bibliothek bietet. Eine gute Implementierung der C-Bibliothek unterstützt praktisch jede Codierung, die die meisten Programmierer jemals benötigen werden (auf einem meiner Systeme habe ich Zugriff auf 221 eindeutige Codierungen).wchar_t
breit genug ist, um alle von der Implementierung unterstützten Zeichen aufzunehmen. Dies bedeutet (mit möglicherweise einer bemerkenswerten Ausnahme), dass die meisten Implementierungen sicherstellen, dass sie breit genug sind, dass ein Programm, das verwendetwchar_t
, alle vom System unterstützten Codierungen verarbeitet (Microsoftwchar_t
ist nur 16 Bit breit, was bedeutet, dass ihre Implementierung nicht alle Codierungen vollständig unterstützt). vor allem die verschiedenen UTF-Codierungen, aber ihre ist die Ausnahme, nicht die Regel).Diese FAQ ist eine Fülle von Informationen. Zwischen dieser Seite und diesem Artikel von Joel Spolsky haben Sie einen guten Anfang.
Eine Schlussfolgerung, zu der ich unterwegs gekommen bin:
wchar_t
ist 16 Bit unter Windows, aber nicht unbedingt 16 Bit auf anderen Plattformen. Ich denke, es ist ein notwendiges Übel unter Windows, kann aber wahrscheinlich woanders vermieden werden. Unter Windows ist es wichtig, dass Sie Dateien verwenden müssen, deren Name Nicht-ASCII-Zeichen enthält (zusammen mit der W-Version der Funktionen).Beachten Sie, dass Windows-APIs, die
wchar_t
Zeichenfolgen verwenden, eine UTF-16-Codierung erwarten. Beachten Sie auch, dass dies anders ist als UCS-2. Beachten Sie die Ersatzpaare. Diese Testseite enthält aufschlussreiche Tests.Wenn Sie sich die Programmierung unter Windows können Sie nicht verwenden
fopen()
,fread()
,fwrite()
usw. , da sie nur nehmenchar *
und nicht UTF-8 - Codierung verstehen. Macht die Portabilität schmerzhaft.quelle
f*
und Freundechar *
auf jeder Plattform arbeiten, weil der Standard dieswcs*
vorschreibt - verwenden Sie stattdessen wchar_t.So führen Sie eine strikte Unicode-Programmierung durch:
strlen
,strcpy
... aber ihre Pendants Widewstrlen
,wsstrcpy
...)Multi-Byte-Zeichenfolgen sind eine Codierung, die vor der UTF-16-Codierung (die normalerweise verwendet wird
wchar_t
) datiert, und es scheint mir, dass sie eher nur Windows ist.Ich habe noch nie davon gehört
wint_t
.quelle
Das Wichtigste ist, immer klar zwischen Text und Binärdaten zu unterscheiden . Versuchen Sie, dem Modell von Python 3.x
str
vs.bytes
oder SQLTEXT
vs. zu folgenBLOB
.Leider verwirrt C das Problem, indem es
char
sowohl für "ASCII-Zeichen" als auch für "ASCII-Zeichen" verwendetint_least8_t
. Sie möchten etwas tun wie:Möglicherweise möchten Sie auch Typedefs für UTF-16- und UTF-32-Codeeinheiten, dies ist jedoch komplizierter, da die Codierung von
wchar_t
nicht definiert ist. Sie benötigen nur einen Präprozessor#if
. Einige nützliche Makros in C und C ++ 0x sind:__STDC_UTF_16__
- Wenn definiert,_Char16_t
existiert der Typ und ist UTF-16.__STDC_UTF_32__
- Wenn definiert,_Char32_t
existiert der Typ und ist UTF-32.__STDC_ISO_10646__
- Wenn definiert,wchar_t
ist UTF-32._WIN32
- Unter Windowswchar_t
ist UTF-16, obwohl dies den Standard bricht.WCHAR_MAX
- Kann verwendet werden, um die Größe von zu bestimmenwchar_t
, aber nicht, ob das Betriebssystem es zur Darstellung von Unicode verwendet.Siehe auch:
Nein. UTF-8 ist eine perfekt gültige Unicode-Codierung, die
char*
Zeichenfolgen verwendet. Dies hat den Vorteil, dass Sie überhaupt keine Änderungen vornehmen müssen , wenn Ihr Programm für Nicht-ASCII-Bytes transparent ist (z. B. ein Konverter mit Zeilenende, der auf andere Zeichen einwirkt\r
und\n
diese unverändert durchläuft).Wenn Sie sich für UTF-8 entscheiden, müssen Sie alle Annahmen ändern, die
char
= Zeichen (z. B. keinetoupper
Schleife aufrufen ) oderchar
= Bildschirmspalte (z. B. zum Umbrechen von Text).Wenn Sie sich für UTF-32 entscheiden, haben Sie die Einfachheit von Zeichen mit fester Breite (aber keine Grapheme mit fester Breite , sondern müssen den Typ aller Ihrer Zeichenfolgen ändern).
Wenn Sie sich für UTF-16 entscheiden, müssen Sie sowohl die Annahme von Zeichen mit fester Breite als auch die Annahme von 8-Bit- Codeeinheiten verwerfen , was dies zum schwierigsten Upgrade-Pfad für Einzelbyte-Codierungen macht.
Ich würde empfehlen, aktiv zu vermeiden,
wchar_t
da es nicht plattformübergreifend ist: Manchmal ist es UTF-32, manchmal ist es UTF-16 und manchmal ist es eine ostasiatische Codierung vor Unicode. Ich würde empfehlen, zu verwendentypedefs
Noch wichtiger ist, vermeiden
TCHAR
.quelle
char *
können Probleme haben, wenn sie einconst char *
letztes bestanden haben, an das ich mich erinnere (aber ich bin vage darüber und welche Funktionen nehmen Sie es mit einer Prise Salz). Nur weil es mit anderen Sprachen komplizierter ist, heißt das nicht, dass es ein schlechtes Design ist.Ich würde keiner Standardbibliotheksimplementierung vertrauen. Rollen Sie einfach Ihre eigenen Unicode-Typen.
quelle
Grundsätzlich möchten Sie Zeichenfolgen im Speicher als
wchar_t
Arrays anstelle von char behandeln. Wenn Sie irgendeine Art von E / A ausführen (wie das Lesen / Schreiben von Dateien), können Sie mit UTF-8 (dies ist wahrscheinlich die häufigste Codierung) codieren / decodieren, was einfach zu implementieren ist. Google einfach die RFCs. Im Speicher sollte also nichts mehr als Byte sein. Einerwchar_t
repräsentiert ein Zeichen. Wenn Sie jedoch zur Serialisierung kommen, müssen Sie in etwas wie UTF-8 codieren, in dem einige Zeichen durch mehrere Bytes dargestellt werden.Sie müssen auch neue Versionen von
strcmp
usw. für die breiten Zeichenketten schreiben , aber dies ist kein großes Problem. Das größte Problem wird die Interaktion mit Bibliotheken / vorhandenem Code sein, die nur Char-Arrays akzeptieren.Und wenn es darum geht
sizeof(wchar_t)
(Sie benötigen 4 Bytes, wenn Sie es richtig machen wollen), können Sie es jederzeit mittypedef
/macro
hacks auf eine größere Größe neu definieren, wenn Sie müssen.quelle
Soweit ich weiß, ist wchar_t implementierungsabhängig (wie aus diesem Wiki-Artikel hervorgeht ). Und es ist kein Unicode.
quelle