UCS-2, UTF-8, UTF-16, UTF-32, WTF?


  • Mod

    @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 wie somestring[a] == 'b' oder auto 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 wie somestring[a] == 'b' oder auto 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.



  • @DocShoe

    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 in cchar_t konvertiert (wchar_t plus Attribute fürs Terminal: Farbe, Stil …). D.h. es gibt auch APIs, die mit wchar_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 ein str.back() == '?'. Mit UTF-8 geht sogar noch strchr (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 ein begins_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 definiert wchar_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.



  • @firefly sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:

    Mit der aussage wäre ich vorsichtig 🙂 Aktuell mag 32Bit für alle aktuellen Unicode Zeichen reichen.

    Es sind ja keine 32Bit sondern deutlich weniger, aktuell wird nur bis 0x0010FFFF kodiert. Die ursprüngliche Version erlaubte noch Codes bis 0x7FFFFFFF. Das kann man gut in der RFC-2279 und RFC-3629 nachlesen.



  • @firefly sagte in UCS-2, UTF-8, UTF-16, UTF-32, WTF?:

    @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.

    Der Unicode Codespace ist als 0 ... 0x10FFFF definiert. UTF-16 kann auch gar nicht mehr darstellen. UTF-8 streng genommen auch nicht, da Bytesequenzen die Codepoints > 0x10FFFF ergeben würden als ungültig definiert sind.

    Trotz dem wir schon tonnenweise Müll (z.B. Emojis) reindefiniert haben und ein paar richtig dicke Dinger wie Han oder Hangul dabei sind, sind nichtmal 30% des Codespace verwendet.

    Den Codespace zu erweitern wäre auch ein "breaking change". Man könnte auch sagen dass sich daran nie etwas ändern kann, weil das was man dann hätte nicht mehr Unicode wäre. Weiters könnten wir dann viele der existierenden Programme mit Unicode Support in the Tonne treten.

    Also ich würde mir da keine grossen Sorgen machen.


Log in to reply