sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von fat-lobyte » Fr Apr 17, 2009 9:19 am

Hallo!

Ich schreibe gerade an einem kleinen Stückchen Code, das Nachrichten über das Netzwerk verschicken und empfangen kann.
Dieses Stückchen Code sollte so portabel wie möglich sein, also auf allen gängigen Systemen laufen, in möglichst vielen Ländern mit möglichst vielen Sprachen. Um dieses Ziel zu realisieren, habe ich in diesem Code für allen Text, der vom User kommen oder für den User bestimmt sein könnte als std::wstring definiert.
In der Annahme, meine Strings wären nun doppelt so groß wie normalerweise habe ich glücklich weiterprogrammiert. Bis ich eines Tages durch Zufall folgende Entdeckung gemacht habe:

Code: Alles auswählen

#include <iostream>
#include <string>

int main()
{ std::cout<<"sizeof(wchar_t): "<<sizeof(wchar_t)<<'\n'; }
Dieser Code gibt auf Windows Erwarteterweise folgenden Text aus:
sizeof(wchar_t): 2
Auf Linux staunte ich allerdings nicht schlecht:
sizeof(wchar_t): 4
4 Bytes?? Ein Zeichen und vier Bytes? "Wow", dachte ich mir. Das ist ziemlich viel. Aber na gut, damit kann ich leben dachte ich mir. So groß sollten die Nachrichten auch wieder nicht sein. Irgendwie hatte ich den Unterschied zwischen den beiden Plattformen unterschlagen, und jedes Zeichen einfach in die entsprechende Anzahl von Bytes verpackt und verschickt. Auf der Empfängerseite kam alles so an wie es sollte. Ich habe ja nur auf Linux getestet.

Tjo, und wie ich heute dazu komme meine Verpack- und Entpackroutinen neu zu schreiben kam ich darauf, dass das ganze nicht so naiv und unproblematisch ist.

Folgendes Problem:

Situation a)
sizeof(wchar_t)(Sender) == sizeof(wchar_t)(Empfänger)
Kein Problem, einfach alle Bytes eines Zeichens in die Nachricht packen und dann wieder alle Bytes aus der Nachricht in die Zeichen.

Situation b)
sizeof(wchar_t)(Sender) > sizeof(wchar_t)(Empfänger)
Tja... Da kommt nun eine Nachricht mit mehr bytes pro Zeichen als ich verarbeiten kann. Wohin damit? Einfach abschneiden? Konvertieren? Wie? Mit was?

Situation c)
sizeof(wchar_t)(Sender) < sizeof(wchar_t)(Empfänger)
Mein Zeichen hat nun mehr Bytes als ich aus dem Netzwerk bekomme. Was mache ich mit den Fehlenden? Einfach ausnullen?


Erwähnt sein noch, dass ich vorhatte Unicode als Zeichenkodierung zu verwenden.

Meine Frage:
Was sollte ich eurer Meinung nach tun? Soll ich überschüssiges abschneiden und fehlendes einfach mit Nullen ersetzen? Funktioniert das auch mit der Unicode kodierung? Muss ich jetzt nachsehen wie UTF-16 und UTF-32 kodiert ist und eigene Konvertierungsroutinen schreiben?
Sollte ich vielleicht sogar einen eigenen Stringtyp definieren, dessen Zeichengröße auf jeder Plattform gleich ist? Wie groß dann? 4 oder 2 Bytes?
Kurz: Was soll ich tun?

In Hoffnung auf gute Ratschläge,
fat-lobyte

edit: ps.: Könnte vielleicht jemand der nicht Linux und nicht Windows hat (z.B. Mac) das kurze Programm kompilieren und laufen lassen? Wüsste zu gern, wie groß sizeof(wchar_t) beispielsweise auf einem Mac ist.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von Kerli » Fr Apr 17, 2009 9:41 am

fat-lobyte hat geschrieben:Kurz: Was soll ich tun?
Also ich würde mich da nicht auf eine bestimmte Länge fixieren und auch keine eigenen Routinen schreiben. Das könnte sonst recht aufwendig sein. Ich hab dazu aber einen interessanten Artikel und darin auch eine interessante Opensource Bibliothek gefunden.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
+Fuss+
Beiträge: 385
Registriert: Fr Nov 14, 2008 8:16 pm

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von +Fuss+ » Fr Apr 17, 2009 11:23 am

Also das mit dem Konvertieren ist schwierig, weil eben zu viele oder zu wenige bytes ankommen können. Gibt es da nicht noch eine Variable, die auf "allen" Systemen die gleiche Größe hat?

PS: Intrepid sagt 4! Windows seh ich heute Abend und Mac habe ich keinen hier.

MfG Fuss

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von Dirty Oerti » Fr Apr 17, 2009 12:08 pm

Hardy meint auch 4.

Ich würde es für das beste halten, wenn du dir selbst eine abgespeckte String-Übertragunsklasse schreibst, mit Funktionen zum konvertieren.

Die 2 Byte-Zeichen konvertierst du dann zu 4 Byte-Zeichen, wobei die zusätzlichen 2 Byte einfach auf 0 gesetzt werden.
Die 4 Byte-Zeichen konvertierst du in 2 Byte-Zeichen (einfach die beiden zusätzliche abschneiden). Wenn die beiden zusätzlichen Bytes != null waren, dann heißt dass, dass du das nicht als 2 Byte darstellen kannst. => Du zeigst ein Zeichen, dass genau das ausdrücken soll (z.B. ein Viereck o.ä.)

Das einzige, worauf du dann nur achten musst ist die Byte-Order.
Aber das gibt es ja Funktionen, die das konvertieren. Du musst dich halt auf eine bestimmte festlegen :)
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von nufan » Fr Apr 17, 2009 1:27 pm

Für genau diesen Fall gibts in SDL Uint16 und Uint32. Weil man sich einfach nicht sicher sein kann, dass ein int auf jedem Rechner gleich ist. Ich weiß nicht wie genau dein Programm schreibst (Konsolenprogramm?). In SDL ist der Wert als Uint16 in der Event-Struktur: event.key.keysym.unicode. Hab das vor kurzem in ETB eingebaut, war sogar einfacher als Events in ASCII zu konvertieren.

FreeBSD sagt auch 4 Byte.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von fat-lobyte » Fr Apr 17, 2009 2:33 pm

Dirty Oerti hat geschrieben:Hardy meint auch 4.

Ich würde es für das beste halten, wenn du dir selbst eine abgespeckte String-Übertragunsklasse schreibst, mit Funktionen zum konvertieren.
Dirty Oerti hat geschrieben:Die 2 Byte-Zeichen konvertierst du dann zu 4 Byte-Zeichen, wobei die zusätzlichen 2 Byte einfach auf 0 gesetzt werden.
Die 4 Byte-Zeichen konvertierst du in 2 Byte-Zeichen (einfach die beiden zusätzliche abschneiden).
Das ist die Holzhammermethode, an die habe ich auch schon gedacht. Die Frage ist nur, ob es ausreicht das Zeichen einfach nur mit Nullen zu füllen bzw. einfach nur abzuschneiden. Ich kenne UTF-16 und UTF-32 nicht, aber ich glaube das geht nicht so einfach. Bestimmte Zeichen in UTF-32 können in einem UTF-16 Zeichen nicht dargestellt werden, sie müssen in zwei oder drei UTF-16 Zeichen Platz finden. Deswegen hat ein Abschneiden zur Folge dass Zeichen einfach ungültig oder Falsch sind, und das ohne Fehlermeldungen. (Wenn ich Fehlermeldungen wollte müsste ich Dinge überprüfen, das würde wieder Programmierzeit und Laufzeit kosten.) Kurz, ich denke diese Lösung ist eher "suboptimal"
Dirty Oerti hat geschrieben:Wenn die beiden zusätzlichen Bytes != null waren, dann heißt dass, dass du das nicht als 2 Byte darstellen kannst. => Du zeigst ein Zeichen, dass genau das ausdrücken soll (z.B. ein Viereck o.ä.)
Das bedeutet, dass der Algorithmus zum "serialisieren" (aus irgend einem Objekt ein Abfolge von Bytes machen) zu kompliziert wird. Außerdem ist die Darstellung nicht mein Bier - und das sollte sie auch nicht werden. Dinge wie Darstellung sollten von diversen Bibliotheken abgenommen werden. Ich habe in diesem Fall wxWidgets vertraut.
Dirty Oerti hat geschrieben:Das einzige, worauf du dann nur achten musst ist die Byte-Order.
Richtig, die gibts ja auch noch ^^
dani93 hat geschrieben:Für genau diesen Fall gibts in SDL Uint16 und Uint32. Weil man sich einfach nicht sicher sein kann, dass ein int auf jedem Rechner gleich ist.

Das ist nicht das Problem, die habe ich auch zur Verfügung ( boost::uint16, boost::uint32 ). Die Frage ist nur ob ich das tatsächlich fix lassen will. Bis jetzt wird es aber darauf hinauslaufen.
dani93 hat geschrieben:Ich weiß nicht wie genau dein Programm schreibst (Konsolenprogramm?).
Das ist nicht wichtig. Hier geht es um den Teil des Codes, der aus einem std::wstring einen std::vector<unsigned char> macht und umgekehrt.
dani93 hat geschrieben:FreeBSD sagt auch 4 Byte
Gut, das ist interessant. Würd noch gern wissen wie das auf MacOS aussieht. Aber ich kanns mir schon denken.

Bis jetzt sehe ich 2 Möglichkeiten:
1. Ich verwende statt std::wstring einfach std::string. Das bedeutet pro Zeichen nur ein Byte, also wirds UTF-8 werden. Die Kodierung wird wxWidgets überlassen, daraus einen Byte- Vektor zu machen ist auch ganz einfach.

2. Ich Verwende nun ein größeres Zeichen, sagen wir uint16. Dann müsste ich allerdings eigene Character Traits schreiben um einen basic_string daraus zu machen. Für die jeweiligen Systeme müsste ich dann aber auch noch die Byte Order hin und her konvertieren...

Was ich aber auf jede Fall machen werde ist einmal alle strings durch ein typedef zu ersetzen, und alle operationen soweit zu abstrahieren dass nur mehr das Sequence Concept benötigt wird (um evtl. auch auf einen std::vector<char> umzusteigen.


Kritikpunkte? Weitere Ideen?
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von Xin » Sa Apr 18, 2009 7:35 am

fat-lobyte hat geschrieben:Das ist die Holzhammermethode, an die habe ich auch schon gedacht. Die Frage ist nur, ob es ausreicht das Zeichen einfach nur mit Nullen zu füllen bzw. einfach nur abzuschneiden. Ich kenne UTF-16 und UTF-32 nicht, aber ich glaube das geht nicht so einfach. Bestimmte Zeichen in UTF-32 können in einem UTF-16 Zeichen nicht dargestellt werden, sie müssen in zwei oder drei UTF-16 Zeichen Platz finden.
Was möchtest Du denn übertragen? UTF16 oder UTF32?
Entsprechend definierst Du Dir Deinen eigenen Datentyp, den Du für die Übertragung nutzt.

fat-lobyte hat geschrieben:Das ist nicht das Problem, die habe ich auch zur Verfügung ( boost::uint16, boost::uint32 ). Die Frage ist nur ob ich das tatsächlich fix lassen will. Bis jetzt wird es aber darauf hinauslaufen.
Ich würde mich auf eins festlegen. Wenn Du Texte transportierst ist die Verbindungsanfrage teuer, der Transfer von x oder 2x Daten macht bei Text keinen Unterschied mehr.
fat-lobyte hat geschrieben:Bis jetzt sehe ich 2 Möglichkeiten:
...
Kritikpunkte? Weitere Ideen?
Möglichkeit 1 sieht nach weniger Aufwand aus und nach leicht nachvollziehbaren Elementgrößen.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: sizeof(wchar_t), Unicode und UTF-16 zu UTF-32 und zurück

Beitrag von fat-lobyte » Sa Apr 18, 2009 3:23 pm

Danke für eure Hilfe. Der Transportlayer wird jetzt einmal mit Zeichen einer Fixierten Größe gecshrieben. Welche Größe das ist, UTF-8, UTF-16 oder UTF-32 soll jetzt einmal unentschieden bleiben.

Großes thx,
fat-lobyte
Haters gonna hate, potatoes gonna potate.

Antworten