Sonderzeichen und Char



  • Hallo zusammen,

    ich lese aus einer Datei einen String, der Sonderzeichen wie ä,ą, ę, ö, ß enthält. Solange ich mit string arbeite, passt alles wunderbar, doch an einer Stelle, wandel ich sie in einen char um. Hier kracht es dann, weil char damit nichts anfangen kann. Das merke ich dann, wenn das Ergebnis in die Ausgabedatei geschrieben wird.

    void permutation(int k, string &s) {
    	char tmp;
        	for(int j = 1; j < s.length(); ++j) {
            	tmp = s[k%(j+1)]; //Sonderzeichen, das ich wieder später in den String an andere Stelle schreibe --> komisches Zeug
           	 	//..
        }
    }
    

    Wie kann ich dieses Problem beheben? Zwar kenne ich wstring, doch bezweifel ich, dass dieser mir hier helfen kann. Es liegt ja am char, denn ich in einen fstream schiebe.

    Vielen Dank
    LG, freakC++



  • Welches Encoding hat denn die Datei? UTF8 solltest du dann besser nicht byteweise bearbeiten.



  • der Grund wiso du "mist" bei der Ausgabe bekommst ist der,
    dass die Sonderzeichen im string als UTF-8 kodiert sind.
    Und bei UTF-8 kann ein Zeichen aus mehr als einem byte (char) bestehen.

    Entweder du wandelst den string nach wstring (wchar_t -> UTF-16 kodierung unter Windows oder UTF32 Kodierung unter Linux => ein element im string = ein Zeichen)

    Oder du musst deinen Algorithmus um die Erkennung/Verarbeitung von UTF-8 codepoint sequenzen erweitern


  • Mod

    firefly schrieb:

    d
    Entweder du wandelst den string nach wstring (wchar_t -> UTF-16 kodierung unter Windows oder UTF32 Kodierung unter Linux => ein element im string = ein Zeichen)

    UTF-16 ist auch eine Multicharcodierung.



  • Hallo freakC++,

    spätestens das ą und ę mit Ogonek lassen sich nicht mehr in einer Variablen vom Typ char unterbringen, da sie diese Zeichen die Codes 0x105 und 0x119 haben (s. Unicode Latin Extended-A).
    Folglich ist es IMHO schon ein Fehler, Text aus dieser Datei in einen std::string zu lesen. Besser wäre der std::wstring und natürlich die entsprechende codecvt-Facette, die dafür sorgt, dass das Encoding korrekt abläuft. Mit so einer Facette ist der Stream vor dem Lesen zu infizieren.
    Wahrscheinlich hast Du eine UTF-8-Datei vor Dir und der neue C++-Standard stellt da glücklicherweise alles notwendig zur Verfügung (s. std::codecvt_utf8).

    Beim Abspeichern ist natürlich die gleiche Facette zu nehmen.

    Gruß
    Werner



  • Aber ein std::string kommt bestens mit ą und ę zurecht.

    int main() {
    
            fstream ffile("zeichen.txt", fstream::in); //In Datei steht das Wort ąąą
            fstream out("ergebnis.txt", fstream::out);
    
            string str;
    
            ffile >> str;
    
            out << str
            cout << str << endl;
    
            return 0;
    }
    

    str wird ohne Fehler in die Ausgabedatei geschrieben, also irgendwie kann string damit umgehen. Das Problem tritt genau dann auch, wenn ich den String manipulieren möchte. Egal, ob ich mit einem Iterator drüber gehe oder die einzelnen Zeichen als Chars ändere, gibt es Probleme.

    Verstehe ich euch richtig, dass ein wstring schon Abhilfe schaffen kann? Ich meine mich zu erinnern, dass es auch am fstream liegt.

    Vielen Dank
    LG, freakC++



  • Du liest ja einfach nur alle Zeichen in den String ein und schreibst sie wieder weg. Klar funktioniert das. Wenn Du aber nun mit [] auf ein einzelnes char zugreifst, dann erwischst Du damit uU eben nur ein Fragment eines UTF-8 - Zeichens.



  • Hallo freakC++,

    freakC++ schrieb:

    Aber ein std::string kommt bestens mit ą und ę zurecht.

    Ein std::string enhält Elemente vom Typ char . Ein char kann Werte im Bereich von 0x00 bis 0xff aufnehmen. Mehr geht nicht. Ein UTF-8-kodiertes Zeichen ą (Code 0x0105) ist in zwei Byte untergebracht (0xC4 0x85). Wie genau das geht, kannst Du bei der UTF-8-Beschreibung nachlesen.

    Lädst Du einen Text mit einem ą aus einer solchen Datei in einem String, kannst Du nun nicht mehr char-weise auf einzelne Zeichen zugreifen, sondern nur auf die Bytes, so wie sie vorher in der Datei standen. Deshalb funktioniert Dein Code oben, der schlicht eine Byte-Kopie erstellt, aber nicht irgendwelche Zeichenoperationen.

    Deshalb passiert genau das:

    freakC++ schrieb:

    Das Problem tritt genau dann auch, wenn ich den String manipulieren möchte. Egal, ob ich mit einem Iterator drüber gehe oder die einzelnen Zeichen als Chars ändere, gibt es Probleme.

    freakC++ schrieb:

    Verstehe ich euch richtig, dass ein wstring schon Abhilfe schaffen kann? Ich meine mich zu erinnern, dass es auch am fstream liegt.

    Richtig - es ist beides notwendig. Zunächst musst Du UTF-8 (bzw. das richtige Encoding!) auch lesen. Das ist Sache des fstreams - genauer der std::codecvt-Facette, die im unterliegenden Filebuf arbeitet. Damit das auch weitgehend funktioniert benötigst Du bereits einen std::wifstream.

    Anbei eine kleine Programmskizze:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <codecvt> // std::codecvt_utf8
    
    int main()
    {
        using namespace std;
        wifstream in( "zeichen.txt" );
        in.imbue( locale( in.getloc(), new std::codecvt_utf8< wchar_t > ) );
    
        wstring txt;
        if( in >> txt ) // erstes Wort lesen
        {
            cout << "Text der Laenge " << txt.size() << " gelesen" << endl;
            // .. hier sind jetzt Zeichenmanipulationen möglich
        }
    
        return 0;
    }
    

    Achtung: das setzt voraus, das die Datei 'zeichen.txt' tatsächlich UTF-8 codiert ist!

    freakC++ schrieb:

    fstream ffile("zeichen.txt", fstream::in); //In Datei steht das Wort ąąą
    

    wie siehst Du das? Editor? Browser?

    Öffne die Datei am Besten mit einem Hex-Editor und poste uns die ersten zwei Zeilen.

    Gruß
    Werner



  • SeppJ schrieb:

    firefly schrieb:

    d
    Entweder du wandelst den string nach wstring (wchar_t -> UTF-16 kodierung unter Windows oder UTF32 Kodierung unter Linux => ein element im string = ein Zeichen)

    UTF-16 ist auch eine Multicharcodierung.

    Richtig, nur im Normalfall wird man kaum in einer Text-Datei Zeichen aus dem Unicode range U+10000 - U+10FFFF finden.

    Und AFAIK gehen viele APIs, welche intern utf-16 (oder vorher UCS-2) als Unicode Kodierung verwenden, davon aus, dass ein 16Bit Wert = ein Zeichen.


  • Mod

    firefly schrieb:

    SeppJ schrieb:

    firefly schrieb:

    d
    Entweder du wandelst den string nach wstring (wchar_t -> UTF-16 kodierung unter Windows oder UTF32 Kodierung unter Linux => ein element im string = ein Zeichen)

    UTF-16 ist auch eine Multicharcodierung.

    Richtig, nur im Normalfall wird man kaum in einer Text-Datei Zeichen aus dem Unicode range U+10000 - U+10FFFF finden.

    Und hältst du dieses Argument für gut?

    Und AFAIK gehen viele APIs, welche intern utf-16 (oder vorher UCS-2) als Unicode Kodierung verwenden, davon aus, dass ein 16Bit Wert = ein Zeichen.

    Nur weil es schlechte APIs falsch vormachen, heißt es nicht, dass es richtig ist.


Log in to reply