Seite 1 von 1

Umlaute in TinyXML2

Verfasst: 11.03.2014, 12:45
von Raven280438
Hi,

ich habe eine wxWidgets Application, welche ich gerne mehrsprachig machen will.

Dazu lese ich eine XML-Datei mit den Textbausteinen ein.
Funktioniert alles, nur gib es Probleme mit Umlauten.
Aus einem Ö wird zB
ö
Die XML-Datei ist als UTF8 gespeichert, die erste Zeile lautet
<?xml version="1.0" encoding="UTF-8"?>
Ich denke das Problem liegt an TinyXML2, da wenn ich in wxWidgets einfach ein Ö ausgebe, wird es richtig angezeigt. Nur halt nicht, wenn ich es mit TinyXMl2 einlesen.

Was kann man da machen?


Gruß

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 13:05
von Schrompf
Schau Dir das File im Hexeditor an. Wenn dort wirklich zwei Bytes stehen, dann ist die Datei halt UTF8 und Du bekommst von TinyXML einen UTF8-String. Der Fehler liegt dann auf Deiner Seite, dass Du mit dem Encoding nicht umgehen kannst.

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 13:12
von Raven280438
Hi,
Der Fehler liegt dann auf Deiner Seite, dass Du mit dem Encoding nicht umgehen kannst.
Ich programmiere noch nicht soo lange C++ und hab mich mit Encoding noch nie wirklich auseinander gesetzt.

Das Projekt ist auf Unicode gestellt.

Wie geht man am besten mit UTF8 um? Was muss man noch beachten?


Gruß

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 14:14
von Schrompf
Worauf das Projekt eingestellt ist, ist hierfür quasi egal. Das ist nur Microsoft-Unterscheidung, ob es Zeichenketten als wchar_t (16Bit pro Zeichen) oder als char (8 Bit pro Code Point) ablegt. Man beachte den Wechsel von "Zeichen" zu "Code Point", das ist ein Unterschied. Diese Einstellung unterscheidet auch, ob jeweils die W oder die A-Variante eines OS-Aufrufs verwendet wird, aber mehr auch nicht.

Encodings in Kurzform: es gibt um die 110 Tausend definierte Schriftzeichen. Siehe http://de.wikipedia.org/wiki/Unicode Bei der Anzahl wird klar, dass die nicht in 8Bit reinpassen, auch nicht in 16Bit. Daher gibt es viele verschiedene Encodings, die besagen, welche Zeichen auf welchem Zahlenwert rauskommen. Die meisten dieser Encodings selektieren dabei sehr stark, ein Großteil der Zeichen kann gar nicht dargestellt werden.

Uralt: Ansi-Code. Enthält nur die Zeichen bis 32-127, also die klassische englische Sichtweise auf die Welt mit 26 großen und kleinen Buchstaben, Zahlen und einigen Satzzeichen.
Dann gibt es viele erweiterte Encodings, die auch nur ein Byte pro Zeichen haben und die oberen 128 Werte für alle möglichen Sonderzeichen benutzen. Standard in Mitteleuropa ist da z.B. http://en.wikipedia.org/wiki/Windows-1252, aber da sieht man bereits, dass damit in z.B. Russland nix mehr zu machen ist.

UTF8 ist ein Encoding, dass ebenso das in ANSI ungenutzte 8. Bit ausnutzt. Nur wird bei UTF8 (vereinfacht ausgedrückt) damit signalisiert, dass ein Zeichen aus mehr als einem Byte besteht. Das ist ein ziemlich cooler Trick, mit dem man einige Millionen Zeichen abbilden kann, aber ein Text wie normales ANSI aussieht, solange er kein Sonderzeichen verwendet. Also quasi eine clevere Art der Abwärtskompatibilität. Das hat auch den Vorteil, dass für ein Programm quasi alle Stringoperationen erstmal wie vorher funktionieren - Suche in Strings, Verketten usw. gehen stressfrei. Nur dass jetzt die Byte-Größe eines Strings nicht mehr besagt, wieviele Zeichen in dem String sind - es können auch weniger Zeichen sein, wenn Zeichen aus mehreren Bytes zusammengesetzt sind.

Der Nachteil ist, dass Du nicht mehr Byte für Byte durch einen String durchgehen kannst und irgendwelche Operationen auf den einzelnen Zeichen ausführen kannst. Das betrifft Dich z.B., wenn Du Deinen Text selbst renderst. Es gibt minimal großen Code, der das Lesen und Schreiben von UTF8-Zeichen übernimmt, aber Du kannst das auch selbst schreiben, wenn Du willst.

Wenn Du in Deinem XML ein "Ö" siehst, bedeutet das folgendes:

- in der XML-Datei steht ein Ö in einem der alten 8Bit-Encodings, also z.B. ein Einzelbyte 214 im Encoding Windows 1252. Wer auch immer Dir den Text anzeigt, hat beim Lesen erraten, dass es Windows1252 ist, und der Zahl 214 das Zeichen "Ö" zugeordnet. Ein russisches Windows z.B. würde wahrscheinlich anders raten, eine andere Codepage auswählen und Dir wahrscheinlich einen russischen Buchstaben stattdessen anzeigen.

- in der XML-Datei steht ein Ö als UTF8, also zwei Bytes mit den Werten, die Du selbst bemerkt hast. Die zumeist automatischen Detektionsalgorithmen erkennen UTF8-Bytefolgen meist recht zuverlässig. Da UTF8 alle Zeichen abbilden kann, gibt es hier keine Codepage, sondern die resultierenden Zahlenwerte können direkt einem Unicode-Zeichen zugeordnet werden. Das funktioniert zuverlässig weltweit, weswegen UTF8 für viele Protokolle und Systeme auf dieser Welt Standard ist.

Wenn Dir wxWidgets ein sauberes Ö dafür anzeigt, dann hat wxWidgets zuverlässig die UTF8-Bytes gelesen und kann sicher Zeichen dazu anzeigen. Andere Programme wie z.B. der Visual Studio-Debugger zeigen erstmal direkt die Einzelbytes an, aber Du kannst sie zumeist auch dazu überreden, es mal mit UTF8 zu versuchen. Dein eigener Code dagegen... das musst Du allein machen. In den meisten Fällen braucht man keine Einzelzeichen und kann UTF8-Strings einfach durchziehen, wie man mag. Bei Einzelzeichen-Verarbeitung wie Tastatureingabe oder eigenem Font Rendering dagegen muss man die UTF8-Dekodierung selber machen.

Alternativ kannst Du auch den Microsoft-bevorzugten Standard UTF16 nehmen. Da werden halt 16Bit-Integer anstatt 8Bit-Integer verwendet, weswegen man nur noch ein oder zwei dieser Code Points verketten muss, um den kompletten Unicode-Zeichensatz abbilden zu können. Das fressen dann alle "W"-Windows-Funktionen, aber dafür musst Du halt UTF8-Strings konvertieren.

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 15:29
von Raven280438
Hi,

danke für die ausführliche Antwort.
Soviel zu Theorie.

Jetzt zur Praxis:
Im Debugger wird das "Ö" tatsächlich falsch angezeigt.
Heist das, ich sollte doch eher std::wstring (das sollte ja UTF16 sein) statt std::string benutzen?

Du schreibst
Microsoft-bevorzugten Standard UTF16
, wie schaut es dann auf Linux und Mac aus?


Gruß

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 15:34
von Schrompf
"Bevorzugt" heißt: es gibt keine Vorschrift. Ich nehme für Splatter z.B. UTF8. Du kannst frei entscheiden, was Du nehmen willst. Du musst halt nur darauf achten, dass all Deine internen Zeichen-Verarbeitung damit arbeiten kann, dass Du alle von außen (Dateien, Netzwerk, Nutzer) ankommenden Texte dazu konvertieren kannst und dass Du alle externen Schnittstellen mit den Daten befüttern kannst, die sie haben wollen.

Linux und Mac bevorzugen UTF8, aber auch das braucht keine Vorschrift für Dich zu sein.

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 16:37
von Spiele Programmierer
Ich würde inzwischen eher UTF-8 empfehlen.
Ich habe bisher meistens UTF-16 verwendet würde davon aber eher abraten. Die meisten Bibliotheken außerhalb der Windows-Only-Welt verwenden UTF-8. Meiner Meinung nach ist UTF-16 inzwischen relativ unsinnig. Wie Schrompf schon richtig gesagt hat, reichen auch die 16 Bits für viele neue Unicodezeichen heute nicht mehr aus. Mir scheint entweder UTF-8 wegen dem durchschnittlich geringeren Speicherplatzverbrauch oder UTF-32 weil ein Zeichen immer aus 4 Bytes besteht(zumindest theoretisch) sinnvoll. UTF-16 ist eine Mischform die außerhalb der WinAPI teilweise umständlich ist und keine der beiden Vorteile wirklich besitzt. Windows scheint mir keine richtige Unterstützung für Unicodezeichen >= 2^16 zu haben. (Siehe zb. hier)

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 17:34
von Raven280438
Hi,

heist das jetzt, ich verwende am besten eine externe Bibliothek (zB ICU)?


Gruß

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 17:52
von Schrompf
Du hast ja immernoch nicht gesagt, was Du denn mit den Texten vor hast, die Du aus dem XML liest. Wenn es nur um schlichte Zeichenverarbeitung geht, z.B. um den Text dann mit einem Fontrenderer auf dem Bildschirm anzuzeigen, würde ich einen kleinen Helfer wie http://utfcpp.sourceforge.net/ empfehlen. Wenn Du komplexe Konvertierungen und Vielsprachen-Support brauchst, z.B. weil Du aus x verschiedenen Mediendateien die ID-Tags auslesen willst (die in dutzenden verschiedener Encodings daherkommen) oder weil Du echte Lokalisierung brauchst, dann ist die ICU eine gute Wahl für Dich.

Ich habe vor ner Weile die ICU integriert, bin aber inzwischen wieder drauf und dran, sie rauszuschmeißen. Der ganze Locale-Workflow ist antiquiriert und brauchen tu ich davon eigentlich nur die UTF8<->UTF16-Konvertierung sowie die schlichte Mehrsprachen-Unterstützung. Und dafür ist die Lib viel zu groß und bringt ihr eigenes Set an reichlich dummen Voraussetzungen mit.

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 17:56
von Raven280438
Hi,

also im Moment möchte ich eine wxWidgets App mehrsprachig machen.

Aber später soll das ganze natürlich auch mit ner Font-Engine laufen


Gruß

Re: Umlaute in TinyXML2

Verfasst: 11.03.2014, 19:55
von Spiele Programmierer
@Schrompf
Nur wegen der UTF-Konversion, finde ich ICU auch etwas viel. C++11 bietet da auch etwas: std::codecvt
Ich weiß natürlich nicht wie die Unterstützung auf dem MSVC aussieht (ich fürchte schlecht), wenn es darum geht.
Wenn das Ziel ist, die WinAPI zu füttern wäre auch "MultiByteToWideChar" eine Wahl. Selber Schreiben ist auch kein großer Akt.

@Raven280438
Im Prinzip ist es wie schon gesagt fast egal.
Prinzipiell würde ich sagen, macht es Sinn die Codierung von den verwendeten Bibliotheken zu verwenden.
In der Doku von wxWidgets steht: http://msdn.microsoft.com/en-us/library ... s.85).aspx
Since wxWidgets 3.0 wxString may use any of UTF-16 (under Windows, using the native 16 bit wchar_t), UTF-32 (under Unix, using the native 32 bit wchar_t) or UTF-8 (under both Windows and Unix) to store its content. By default, wchar_t is used under all platforms, but wxWidgets can be compiled with wxUSE_UNICODE_UTF8=1 to use UTF-8.
Wenn du eine Lösung mit wxWidgets für alle Platformen willst, scheint mir nur UTF-8 als geeignet.
Für das Konvertieren bietet wxWidget scheinbar auch jede Menge Funktionen/Klassen.

EDIT:
Ganz nettes Dokument warum UTF-16 keine gute Idee mehr ist: www.utf8everywhere.org

Re: Umlaute in TinyXML2

Verfasst: 20.03.2014, 14:42
von Raven280438
Hi,

ich hab mir mal das UTF8-CPP angeguckt.

Sorry wenns bald blöd wirkt, aber ich weis immer noch nicht, wie ich weiter vorgehen muss.

Angeblich ist der String, den ich von TinyXML2 bekomme, ja schon UTF8.
Wieso wird er dann nicht richtig angezeigt, wenn wxWidgets auch mit UTF8 umgehn kann.


Für meine Font-Engine brauch ich dann auch den Charcode als Integer. Ein
int charcode = mystring;
reicht da ja wohl nicht. Wie muss ich da weiter vorgehen?

So richtig blick ich da nicht durch.


Gruß

Re: Umlaute in TinyXML2

Verfasst: 20.03.2014, 15:52
von Spiele Programmierer
Hast du in wxWidgets UTF8 konfiguriert?
(Siehe letztes Zitat)
Wenn nicht, umstellen oder konvertieren mit den Methoden von wxWidgets. Ich hatte da in der Doku einiges gefunden.

Re: Umlaute in TinyXML2

Verfasst: 20.03.2014, 20:08
von Brainfreeze
Hi,

ich hab jetzt mal das vorgeschlagene UTFCPP eingebunden.

Die Länge einer UTF8 Zeichenkette bekommt man wohl mit utf8::distance() raus.
Für meine Font-Engine (auf Basis von BMFont) bräuchte ich den Charcode (wie ist der richtige Ausdruck dafür?) der einzelnen UTF8 Zeichen. Leider konnte ich dazu nichts finden.

Kann mir jemand weiterhelfen?

EDIT:
utf8::distance() gibt anscheinend doch nicht das richtige Ergebnis aus.
Gegeben ist der String "ABC ßß", und als Distance bekomm ich 3. Werden da "nur" die UTF8 Zeichen gezählt?



Gruß

Re: Umlaute in TinyXML2

Verfasst: 20.03.2014, 21:39
von waigie
Die Funktion utf8::distance gibt wie du vermutet hast die länge der UTF-8 kodierten Zeichenkette zurück. Jedoch ist dein Test String "ABC ßß" wahrscheinlich nicht UTF-8 kodiert. Der Unicode Codepoint für das Zeichen ß ist U+00DF. Die Darstellung in UTF-8 für das Zeichen ergibt c3 9f. Das Zeichen ß wird mit 2 Byte kodiert.

Hier mein Beispielcode:

Code: Alles auswählen

#include "utf8.h"
#include "iostream"

int main(char** argv, int argc)
{
	char* test = "ABC \xc3\x9f\xc3\x9f";
	try {
		std::cout << utf8::distance(test, test+8) << std::endl;
	} catch(utf8::invalid_utf8 e) {
		std::cout << e.what() << std::endl;
	}
	
}

Re: Umlaute in TinyXML2

Verfasst: 21.03.2014, 09:42
von Raven280438
Hi,

wie kann ich dann für meine Font-Engine die komplette Anzahl an Zeichen + den jeweiligen Charcode ermitteln?

Und, wenn ich einen String, festcodiert im Quellcode, anzeigen lassen möchte, wie kann ich ihn als UTF8 speichern? Muss ich, so wie in deinem Beispiel mit "\xc3\x9f\xc3\x9f" arbeiten?


Gruß