UTF8 kodierte Textdatei einlesen



  • Hallo,
    ich möchte eine UTF8 kodierte Textdatei einlesen, habe allerdings das Problem, dass Umlaute wie z.B. "ü" oder ähnliche nicht korrekt eingelesen werden.

    Weder mit fostream noch mit wofstream bekomme ich es richtig hin.

    Nun habe ich irgendwo gelesen, dass man ggf. die globale locale-Variable ändern müsste. Leider komme ich diesbezüglich nicht weiter.

    Hat jemand vielleicht einen Rat für mich wie ich das am besten bewerkstellige?



  • Eine Google-Suche nach "utf8 c++" bringt dich gleich beim ersten Treffer zu: http://utfcpp.sourceforge.net/



  • Falls du zufällig SFML benutzt, die haben auch eine Abteilung für Unicode 😉
    (und auch sonst nette Features für 2D-Grafik, Sound, Netzwerk, Event-Handling, Threads, Zeitmessung)



  • Ich danke euch für die Antworten - hatte gedacht es gäbe auch relativ einfache Möglichkeit ohne zusätzliche libs auszukommen.
    SFML ist vorhanden und ich werde mich da gleich mal einlesen.

    Vielen Dank nochmals.



  • DateiEinlesen schrieb:

    Hallo,
    ich möchte eine UTF8 kodierte Textdatei einlesen, habe allerdings das Problem, dass Umlaute wie z.B. "ü" oder ähnliche nicht korrekt eingelesen werden.

    Was heißt "nicht korrekt"?



  • Naja, ich meine, dass eben byteweise eingelesen wird und dementsprechend natürlich nicht die kodierten Zeichen eingelesen werden.

    Kleines Beispiel:

    Datei (hex):

    EF BB BF C3 BC
    

    Folgender code

    #include <string>
    #include <fstream>
    
    int main() {
    	std::wstring line;
    	std::wifstream ofs("umlaut.txt");
    	std::getline(ofs,line);
    	return 0;
    }
    

    liest dann in line folgende Zeichenkette ein:
    {

    ü
    

    Bisher hatte ich leider noch keine Zeit, mir SFML bzw. utfcpp näher anzusehen, werde ich wohl erst morgen zu kommen.



  • Gut - also ich habe es jetzt mit UTF8-CPP gelöst:

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <utf8.h>
    
    bool hasBOM (const std::string& str) {
    	return utf8::starts_with_bom(str.begin(),str.end());
    }
    
    int main() {
    	std::string line;
    	std::ifstream ofs("umlaut.txt");
    	std::getline(ofs,line);
    	std::string convertedline;
    	utf8::utf8to16(line.begin()+((hasBOM(line))?3:0), line.end(), std::back_inserter(convertedline));
    	std::cout<<convertedline;
    	std::cin.get();
    	return 0;
    }
    

    So scheint es zu funktionieren.



  • UTF-16 in einem std::string kommt mir seltsam vor. Die einzelnen Zeichen sollten doch 16 Bit ethalten.



  • DateiEinlesen schrieb:

    So scheint es zu funktionieren.

    schau Dir vielleicht auch mal diesen Thread zum Thema UTF8 an.

    Gruß
    Werner



  • Danke Werner Salomon - das funktioniert soweit einwandfrei.
    Allerdings habe ich bezüglich deines dort geposteten Beispiels 2 Fragen:

    file.imbue( locale( file.getloc(), new my::utf8_codecvt_facet() ) );
    

    Entsteht so nicht ein Speicherleck - ich kann das erstellte Facet-Objekt ja nicht mehr löschen.
    Dachte ich mir - gut, dann erstell ich eben vorher das Facet-Objekt und lösch es am Ende:

    my::utf8_codecvt_facet * facet = new my::utf8_codecvt_facet;
    file.imbue( locale( file.getloc(), facet ) ); 
    ...
    delete facet;
    

    Allerdings erhalte ich dann eine Speicher-Zugriffsverletzung.
    Hatte mir voher noch das zuvor gebundene locale gespeichert und dieses auch wieder vor dem delete an file gebunden - allerdings änderte das nichts an der Fehlermeldung.



  • Also generell find ich UTF8-CPP schön.
    Es ist eine schlanke Library, die leicht verwendbar ist:
    http://utfcpp.sourceforge.net/

    Falls du jedoch C++0x verwendest, muss ich dir (im Moment) leider davon abraten,
    da du Kompilier-Fehler wegen eines Nameskonfliktes kriegst, wenn du bestimmte
    Funktionen verwendest.

    Gruß,
    XSpille



  • Alternativ tuts vielleicht auch libiconv?



  • DateiEinlesen schrieb:

    Danke Werner Salomon - das funktioniert soweit einwandfrei.
    Allerdings habe ich bezüglich deines dort geposteten Beispiels 2 Fragen:

    file.imbue( locale( file.getloc(), new my::utf8_codecvt_facet() ) );
    

    Entsteht so nicht ein Speicherleck - ich kann das erstellte Facet-Objekt ja nicht mehr löschen.
    Dachte ich mir - gut, dann erstell ich eben vorher das Facet-Objekt und lösch es am Ende:

    my::utf8_codecvt_facet * facet = new my::utf8_codecvt_facet;
    file.imbue( locale( file.getloc(), facet ) ); 
    ...
    delete facet;
    

    Allerdings erhalte ich dann eine Speicher-Zugriffsverletzung.
    Hatte mir voher noch das zuvor gebundene locale gespeichert und dieses auch wieder vor dem delete an file gebunden - allerdings änderte das nichts an der Fehlermeldung.

    http://www.cplusplus.com/reference/std/locale/locale/facet/
    Die facet Basisklasse hat einen eingebauten "Referenzzähler". Du brauchst (darfst) das also nicht löschen, bzw. dem Ctor 1 übergeben, wenn du es weiterverwenden willst.



  • XSpille schrieb:

    Also generell find ich UTF8-CPP schön.

    Ich gar nicht. In der Dokumentation ist so rein gar nichts von streambuf und codecvt-Facette zu Lesen. Und genau dort würde ein UTF8-Leser/Schreiber hin gehören (siehe boost.UTF-8-codecvt unter History).

    Schaut man sich das das Beispiel an, so wird dort zwar fleißig konvertiert, aber wie sollte man denn aus dieser Datei Zahlen und Objekte herauslesen? Der einzige Weg wäre doch nur der, den konvertierten String wieder in einen Stream zurück zuschreiben um anschließend den eigentlichen Inhalt wieder zu lesen. Das ist doch recht umständlich.

    In einer Applikation, die aus einer Datei liest, will ich mich doch gar nicht darum kümmern, wie die Zeichen in der Datei kodiert sind.

    Unter Design-Zielen steht dort:
    1.) Soll mit allen String-Klassen gut zusammenarbeiten. .. was haben Strings mit UTF8-Konvertierung zu tun?
    4.) Unaufdringlich. Vermeide einen speziellen/aufgezwungenen Programmierstil für den Anwender .. das geht mit der codecvt-Facette wirklich besser.

    Also eine nett gemachte Library, und die Autoren haben sich Mühe gegeben und auch ein schönes Tutorial erstellt, aber leider haben sie es versäumt, sich mit der input/output-Library von C++ auseinander zu setzen. Und ich befürchte, es ist nicht die einzige Library zu dem Thema die so arbeitet.

    Gruß
    Werner



  • Hallo,

    Das mag sein, dass das mit der boost codecvt eleganter geht. Allerdings muss dann eben boost verwendet werden. Mir z. Bsp. ist das nicht erlaubt. Dann ist die UTF8-CPP schon eine gute Lösung.



  • Braunstein schrieb:

    Hallo,

    Das mag sein, dass das mit der boost codecvt eleganter geht. Allerdings muss dann eben boost verwendet werden. Mir z. Bsp. ist das nicht erlaubt. Dann ist die UTF8-CPP schon eine gute Lösung.

    Das ist aber auch eine merkwürdige Regelung: gut getestet boost-Bibliotheken verbieten, aber bei sourceforge aus dem Klo gezogene erlauben 😉



  • Braunstein schrieb:

    Das mag sein, dass das mit der boost codecvt eleganter geht.

    nicht boost.codecvt sondern std::codecvt. boost ist nur ein möglicher Lieferant einer konkreten codecvt für die UTF-8-Konvertierung. Du kannst auch die aus Code Project nehmen oder das komplette Paket bei Dinkumware kaufen.

    Braunstein schrieb:

    Allerdings muss dann eben boost verwendet werden.

    muss nicht (s.o.)

    Braunstein schrieb:

    Mir z. Bsp. ist das nicht erlaubt.

    Darf man nach dem Grund fragen? Das würde mich interessieren.

    Braunstein schrieb:

    Dann ist die UTF8-CPP schon eine gute Lösung.

    nein, da diese Lib zu einem nicht-Standard-konformen Vorgehen verführt.

    Gruß
    Werner


Anmelden zum Antworten