UCS-2, UTF-8, UTF-16, UTF-32, WTF?
-
Ich bin ein Fan von https://utf8everywhere.org/ - die haben auch einen Abschnitt für Windows: https://utf8everywhere.org/#windows
-
@firefly sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Wenn du mit 'Ä' meinst dass es aus einem 8Bit (UTF-8), 16Bit (UTF-16) oder 32Bit (UTF-32) "block" besteht, dann wirst du selbst bei UTF-32 nicht einfach danach suchen können.
Bzw. es als ein Codepoint betrachten könnenDoch, schon. Die UTF-32 Kodierung sieht immer 4 Byte pro Zeichen vor.
Edit:
Ächz... hast recht, auf wikipedia kann man lesen, dass z.B. bestimmte vietnamesische Zeichen auch mit vier Byte nicht auskommen.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
@firefly sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Wenn du mit 'Ä' meinst dass es aus einem 8Bit (UTF-8), 16Bit (UTF-16) oder 32Bit (UTF-32) "block" besteht, dann wirst du selbst bei UTF-32 nicht einfach danach suchen können.
Bzw. es als ein Codepoint betrachten könnenDoch, schon. Die UTF-32 Kodierung sieht immer 4 Byte pro Zeichen vor.
Nein es sind keine Zeichen sondern codepoints!
Und ein codepoint kann ein zeichen sein. Oder mehrere codepoints können ein Zeichen darstellen.siehe z.b. https://www.fileformat.info/info/unicode/char/00c4/index.htm Punkt "Decomposition"
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Ich versuche mir grade über die Konsequenzen klar zu werden, bevor wir was entscheiden. Zugriffe auf einzelne Zeichen sind schon relativ häufig, allein die Tatsache, dass ein
std::string::back()
das letzte Byte eines Codepoints zurückgeben könnte kann schon zu Problemen führen.Von dieser Einzelzeichensichtweise müssen du und deine Anwendung halt weg. Das ist aber auch unabhängig von der Alternativcodierung, denn diese Sichtweise funktioniert halt nur bei bei Codepagecodierungen wie ASCII.
-
Nur eine kurze Anmerkung zu UTF-8: du kannst Teilstrings in UTF-8 mit nem normalen
strstr
suchen. Das funktioniert, weil UTF-8 so aufgebaut ist dass sämtliche Werte die für das 1. Byte einer Sequenz erlaubt sind nicht für die weiteren Bytes erlaubt sind.
Dadurch kann man z.B. auch einfach das letzte Zeichen eines Strings finden: einfach vom Ende des Strings rückwärts das erste "lead byte" suchen - dort beginnt dann das letzte Zeichen.Aber: um was für eine Art Software geht es denn? Ist das irgendwas in Richtung Textverarbeitung?
-
@hustbaer
Ja, das ist mir schon klar, die UTF-8 Kodierung kenne ich. Nur haben wir an vielen Stellen Überprüfungen wiesomestring[a] == 'b'
oderauto pos = somestring.find( 'a' )
. Solange das alles ASCII ist macht das keine Probleme, aber sobald nach einem nicht-ASCII Zeichen gesucht wird kann´s halt Probleme geben. Um ehrlich zu sein weiß ich nicht mal genau, ob dieser Fall in unserer Software überhaupt auftritt, aber ich dachte, ich frage mal nach, wie andere das lösen.Wir machen Messsoftware, wo viele Dinge per Text einstellbar sind und oft Platzhalter Ersetzungen stattfinden. Da dürfte nix passieren, weil unsere Platzhalter alle ASCII sind.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Nur haben wir an vielen Stellen Überprüfungen wie somestring[a] == 'b' oder auto pos = somestring.find( 'a' ). Solange das alles ASCII ist macht das keine Probleme
Richtig, das funktioniert aber mit UTF-8 auch noch.
aber sobald nach einem nicht-ASCII Zeichen gesucht wird kann´s halt Probleme geben.
Dann musst du halt nach dem String
"ä"
oder"𝄞"
suchen und nicht nach'ä'
und'𝄞'
. Wenn dein Source dann auch in UTF-8 ist und du eine Konvention hast, dass all deine Strings UTF-8 sind, dann ist das kein Problem.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
@hustbaer
Ja, das ist mir schon klar, die UTF-8 Kodierung kenne ich. Nur haben wir an vielen Stellen Überprüfungen wiesomestring[a] == 'b'
oderauto pos = somestring.find( 'a' )
. Solange das alles ASCII ist macht das keine Probleme, aber sobald nach einem nicht-ASCII Zeichen gesucht wird kann´s halt Probleme geben. Um ehrlich zu sein weiß ich nicht mal genau, ob dieser Fall in unserer Software überhaupt auftritt, aber ich dachte, ich frage mal nach, wie andere das lösen.Da Windows mittlerweile UTF-16 nutzt, wirst Du wohl oder übel den Code zu umstellen müssen, dass mit Substrings anstatt Zeichen als Suchmuster gearbeitet wird. Dieser Code funktioniert dann auch mit UTF-8, falls ihr jemals auf Linux oder Gtk, Qt portieren wolltet.
Noch eine Anmerkung zu Länge des kodierten Texts. Die ursprüngliche Version von UTF-8 erlaubte bis zu 6 Zeichen für die Kodierung eines UCS-4 Zeichens. Unicode hat mittlerweile den erlaubten Bereich wieder eingeschränkt, so dass man mit 4 Zeichen auskommen kann. Ob es aber dabei dauerhaft bleiben wird sei einmal dahin gestellt.
-
@DocShoe
Klingt für mich so als ob man gut mit sowohl UTF-8 als auch UTF-16 arbeiten könnte.
UTF-16 ist auf Windows einfacher, aber je nachdem wie viele OS Funktionen man wirklich benötigt, ist UTF-8 auch kein so grosses Problem. Und es würde eine eventuelle Portierung einfacher machen.
-
// deleted
-
Vielen Dank für eure Antworten, die haben mir gezeigt, dass ich mit meinen Überlegungen richtig liege. Von der Idee, Zeichen als einzelne Byte zu behandeln muss man sich dann wohl trennen, wenn man Unicode verwendet. Ich muss jetzt mal prüfen, welche Stringliterale unser Compiler unterstützt und die Entscheidung davon abhängig machen. Es bedeutet aber auf jeden Fall, dass sämtliche WinAPI Aufrufe auf die W-Version geändert werden müssen.
-
UTF-8 nach außen habe ich mir auch schon überlegt, damit müssten wir unsere Datenformate nicht ändern, sondern nur wissen, ob es sich bei einer Datei um eine ANSI oder UTF-8 Datei handelt. Ist zwar etwas Arbeit, aber überschaubar.
Bei solchen Fragen muss man auch die älteren Versionen im Umlauf mit berücksichtigen.
- Was passiert wenn beispielsweise Kunde XYZ von der alten auf die neue Version aktualisiert und dieser Daten im alten Format (Konfigurationen, Dateien) laden möchte?
- Müssen Daten bzw. Dateien (z.B. Rechnungsdaten) aufwärtskompatibel bleiben?
- Was passiert wenn der Kunde mit alter Programmversion Daten bzw. Dateien im neueren Format laden möchte? Absturz?
Dadurch muss evt. deine Software Daten bzw. Dateien im alten und neuen Format einlesen können. Denn UTF-8 hin oder her, gibt der Kunde beispielsweise in der Rechnungsadresse Tüpfelhyänenöhrchenstraße 13 an, so wird nach der UTF-8 Interpretation Tüpfelhy䮥nöhrchenstraߥ 13 daraus.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Von der Idee, Zeichen als einzelne Byte zu behandeln muss man sich dann wohl trennen, wenn man Unicode verwendet.
Jein, man kann natürlich intern mit
wchar_t
arbeiten und muss so nicht den Programmcode abändern, aber man konvertiert dann ständig zwischen Programm interner Kodierung (UCS-4) und API Kodierung (UTF-8 bzw. UTF-16). Ob das so sinnvoll ist?Da es letztlich im Forum die Frage nach ncurses gab (hat natürlich nichts mit Windows zu tun), habe ich mir die ncursesw Library angeschaut. Da wird immer
wchar_t
an die Library übergeben und incchar_t
konvertiert (wchar_t
plus Attribute fürs Terminal: Farbe, Stil …). D.h. es gibt auch APIs, die mitwchar_t
arbeiten. Weshalb Windows ausgerechnet UTF-16 verwendet ist mir persönlich schleierhaft, weil es weder die Vorzüge einer gleichen API bietet wie UTF-8 aber nicht problemlos mit allen Unicodezeichen funktioniert wie UCS-4. Windows erschien mir noch nie logisch durchdacht zu sein.
-
@Quiche-Lorraine:
Unsere Dateien tragen alle eine Versionsnummer, dann muss man strings beim Einlesen abhängig von der Versionsnummer behandeln.@john-0
Ne, wchar_t hilft da nicht weiter, weil man bei UTF-16 ähnliche Probleme wie bei UTF-8 hat, nämlich dass ein sich Zeichen aus bis zu zwei wchar_t zusammensetzen darf. Bis Windows2000 hat Microsoft UCS-2 benutzt, dabei wurde jedes Zeichen mit genau 2 Byte kodiert.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Unsere Dateien tragen alle eine Versionsnummer, dann muss man strings beim Einlesen abhängig von der Versionsnummer behandeln.
Wird dann spannend die Versionsnummer zu lesen wenn man noch nicht weiss welches Encoding das File verwendet. Ich würde aber empfehlen für Files auf jeden Fall UTF-8 zu verwenden - selbst wenn ihr euch entschiesst im Speicher UTF-16 zu verwenden. Dann ist das mit Versionsnummer lesen vermutlich auch kein grosses Problem, weil UTF-8 ja ASCII kompatibel ist. (Gut, zu prüfen ob ein Textfile welches z.B. garantiert irgendwo auch ASCII Zeichen enthält UTF-16 ist, ist jetzt auch nicht so schwer. Aber wer will schon UTF-16 Textfiles verwenden?)
Von der Idee, Zeichen als einzelne Byte zu behandeln muss man sich dann wohl trennen, wenn man Unicode verwendet.
Das kommt drauf an was man mit den Zeichen machen will, und um welche Zeichen es sich handelt. Ein
str[0] == '#'
oder so funktioniert auch ohne Probleme mit UTF-8 oder UTF-16. Genau so einstr.back() == '?'
. Mit UTF-8 geht sogar nochstrchr
(und mit UTF-16 wcschr`) so lange man nur die Position im Speicher braucht und nicht wissen muss um den wievielten Codepoint im String es sich handelt.Wenn man dagegen etwas ala
str[0] == userDefinedCharacter
hat, dann ja, dann wird das eine Spur komplizierter. Dann muss da einbegins_with(str, userDefinedCharacter)
statt dessen hin.
-
@hustbaer
Das sind keine Textdateien, sondern Binärdateien, die unter anderem auch Text enthalten. Du bist vermutlich noch bei deiner Textverarbeitung. Der Dateiheader hat eine ASCII Signatur und unter anderem 4 uint für Major, Minor, Release und Build Version. Den Header kann ich also immer richtig lesen und anhand der Versionsnummer weiter entscheiden, wie Text behandelt wird (ANSI/UTF-8). Seh da kein Problem.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Das sind keine Textdateien, sondern Binärdateien, die unter anderem auch Text enthalten. Du bist vermutlich noch bei deiner Textverarbeitung
Naja bis jetzt wurde von dir auch nie gesagt dass es sich um binär daten handelt, welche auch texte enthalten können.
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
Das sind keine Textdateien, sondern Binärdateien, die unter anderem auch Text enthalten.
OK
Du bist vermutlich noch bei deiner Textverarbeitung.
Nönö. Dass du keine Textverarbeitung machst hatte ich schon verstanden. Nur dass heute die meisten Configfiles und auch sehr viele Datenfile Formate textbasiert sind. Und natürlich das was @firefly angedeutet hat: du hattest ja geschrieben dass da Texte drin stehen, und da hab ich dann automatisch Textfile gedacht. Heisst nicht dass du was falsch gemacht hast, nur zur Erklärung wieso ich Textfile angenommen hatte.
Der Dateiheader hat eine ASCII Signatur und unter anderem 4 uint für Major, Minor, Release und Build Version. Den Header kann ich also immer richtig lesen und anhand der Versionsnummer weiter entscheiden, wie Text behandelt wird (ANSI/UTF-8). Seh da kein Problem.
Ich auch nicht
-
@DocShoe sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
@john-0
Ne, wchar_t hilft da nicht weiter, weil man bei UTF-16 ähnliche Probleme wie bei UTF-8 hat, nämlich dass ein sich Zeichen aus bis zu zwei wchar_t zusammensetzen darf. Bis Windows2000 hat Microsoft UCS-2 benutzt, dabei wurde jedes Zeichen mit genau 2 Byte kodiert.Ich sehe gerade, dass UNIX/Linux und Windows
wchar_t
verschieden definieren . Nun denn, die Welt könnte so einfach sein, warum machen wir sie nicht komplizierter. Also, UNIX/Linux definiertwchar_t
als UTF-32 (UCS-4 wurde als Name wohl beerdigt)sizeof(wchar_t)==4
und somit hat man damit immer feste Längen bei std::wstring. Ursprünglich hieß das ganze UCS-4 wurde dann aber mit UTF-16 kompatiblen gemacht und somit entstand dann UTF-32.
-
@john-0 sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:
somit hat man damit immer feste Längen bei std::wstring
Mit der aussage wäre ich vorsichtig Aktuell mag 32Bit für alle aktuellen Unicode Zeichen reichen.
Da aber immer mehr Zeichen dem Unicode Standard hinzugefügt werden, wird irgendwann 32Bit nicht mehr reichen um alle Zeichen kodieren zu können.