Unicode/Ansi: Eure Vorgehensweisen



  • Hallo Artchi und danke für Deine Antwort!

    Artchi schrieb:

    der Dateiname hat nichts mit dem zu tun, was du in den Stream schreibst oder raus liest! Das der Dateiname ist halt noch von 1998 und da waren halt noch ASCII-Dateinamen gängig.
    Das NTFS und Unix-Filesysteme heute vielleicht flexiblere Dateinamen unterstützten, ist kein Beinbruch. Lösung: benutze Boost.Filesystem für den Dateinamen! Und wenn wir Glück haben, sehen wir Boost.Filesystem im C++ TR2.

    Aber kein Grund sich aufzuregen, das angeblich Widecharacter nicht funktionieren.

    Das ist mir bewusst und der Grund ist klar. Mein Problem war nun, dass ich beim Design meiner "Config-Reader-Klassen" nur Unicode-Strings vorgesehen habe. Auch Dateinamen, die ich benötige habe ich in Unicode eingelesen. Als ich diese nun mit std::wfstream öffnen wollte, hatte ich ein Problem. Als Anfänger weiß man das nicht. Es soll auch nicht bedeuten, dass "Widecharacter nicht funktionieren", sondern nur, dass man eben nicht vollständig auf Widecharacter setzen kann. Problematisch wird es vor allen dann, wenn man den Dateinamen auch ausgeben muss und die Ausgabe eben wieder auf Unicode festgelegt ist. Entweder man liest den Dateinamen dann als std::string & std::wstring ein oder man konvertiert z.B. bei der Ausgabe. Man erhält halt einen "Mischbetrieb", den ich in meiner jungfräulichen Naivität verhindern wollte.

    Artchi schrieb:

    std::exception und what():

    Das what() liefert einen technischen Errortext. Und technische Texte sind meistens für den Anwender unbrauchbar. Ein Anwender kann mit "Index out of bounds" nicht wirklich was anfangen. 😉 Exceptions sind zur Programmsteuerung da! Wenn du Chinesische Errortexte haben willst, solltest du diese anhand des Exceptiontyps ausgeben. what() benutze ich für Logdateien.

    Genau, und mein "Logging-Design" setzte eben auch nur auf reines Unicode. Das war mein Problem. Du kannst also doch nicht durchgängig auf Widecharacter-Strings setzten. 😉

    Artchi schrieb:

    Ausgabe-Streams:

    wcin und wcout machen das, was man ihnen sagt. Schau dir mal inbue() an! Meistens ist das locale-Objekt so implementiert, das es einfach alles in Bytes umwandelt. Du mußt dann halt ein anderes Locale-Objekt benutzen, um eine korrekte Ausgabe/EIngabe zu haben. Das ist aber deine Aufgabe, das richtige Locale zu benutzen. Außerdem kann z.B. die Windows-Konsole von Haus aus eh nur ASCII ausgeben.

    Ich benutze Locale-Objekte und trotzdem kann ich mit std::cout z.B. das €-Zeichen ausgeben (aus Windows-Konsole, während es std::wcout nicht kann. Das Gleiche gilt wieder für das Schreiben in eine Textdatei mittels std::(w)fstream.

    Vielleicht liegt der Fehler bei mir, dann wäre ich für ein Code-Beispiel dankbar, welches z.B. das €-Zeichen mittels std::wcout ausgibt.



  • Roger Wilco schrieb:

    Artchi schrieb:

    Außerdem kann z.B. die Windows-Konsole von Haus aus eh nur ASCII ausgeben.

    Ich benutze Locale-Objekte und trotzdem kann ich mit std::cout z.B. das €-Zeichen ausgeben (aus Windows-Konsole, während es std::wcout nicht kann. Das Gleiche gilt wieder für das Schreiben in eine Textdatei mittels std::(w)fstream.

    Auf deutsch: Es geht nicht mit der Windows-Konsole... Sie kann nun mal "nur" ASCII - da ist nichts mit L'€' oder so - '€' hingegen geht aber (weil es nun mal ASCII ist)

    bb



  • unskilled schrieb:

    Auf deutsch: Es geht nicht mit der Windows-Konsole... Sie kann nun mal "nur" ASCII - da ist nichts mit L'€' oder so - '€' hingegen geht aber (weil es nun mal ASCII ist)
    bb

    Ok, musste es eben auch feststellen. Also eher ein "Windows_Problem". Bedeutet also, dass man also bei Windows-Konsolen-Programmen (weiß nicht wie es unter Linux aussieht) std::cout verwenden sollte.

    Ich hätte einfach nicht gedacht, dass ich mit der Entscheidung durchgängig Unicode/wchar-Varianten zu benutzen, solche Probleme bekommen würde.



  • unskilled schrieb:

    Sie kann nun mal "nur" ASCII

    So schlimm ist es auch wieder nicht 😉 Soweit ich weiß, ist die Konsole nur durch die eingestellte Codepage eingeschränkt (fällt mit Verwendung von z.B. UTF-8 weg) und durch die Schriftart (es muss ein Glyph für den Character existieren).



  • Roger Wilco schrieb:

    Ok, musste es eben auch feststellen. Also eher ein "Windows_Problem". Bedeutet also, dass man also bei Windows-Konsolen-Programmen (weiß nicht wie es unter Linux aussieht) std::cout verwenden sollte.

    Ich hätte einfach nicht gedacht, dass ich mit der Entscheidung durchgängig Unicode/wchar-Varianten zu benutzen, solche Probleme bekommen würde.

    Warum kannst du kein wcout benutzen??? Huff... hast du dir überhaupt meinen Link oben angeschaut???

    Natürlich kann die Windows-Konsole auch Euro-Zeichen ausgeben, wenn es sein muß auch Kyrillisch. Es muß nur die richtige Codepage und/oder Font in der Konsole eingestellt sein. Das ist kein Problem von wcout, diese Schwierigkeit bekommst du mit jeder Programmiersprache.

    Und Widecharacter zu benutzen ist kein Fehler. Wenn die Windows-Konsole kein UTF-8 versteht, mußt du halt ein Locale mittels imbue() übergeben, die die Windows-Konsole versteht.

    Standard-mäßig ist in der dt. Windows-Konsole die CP 1252 eingestellt: das EURO-Symbol hat dort den Dezimalcode 128. Eine Locale im Stream müsste also entsprechend die Konvertirung vornehmen, damit du dich nicht drum kümmern bräuchtest. Oder es wird die Codepage in der Konsole umgestellt.

    Und ohne wcout wäre die Situation keinen Deut besser. Das Codepage-Thema würde immer noch da sein.

    Die Linux-Konsolen benutzen meines Wissens die Codepage UTF-8. D.h. du bräuchtest auch dort eine Locale im Stream, die in UTF-8 wandelt.
    Für einen Anfänger alles vielleicht verwirrend, weil tatsächlich umständlich. Les dir einfach mal meinen Link durch, dann wirds vielleicht einfacher.





  • Gib mal in deiner Windows-Konsole chcp ein, damit du erfährst, welche CP eingestellt ist.

    Mit chcp 857 kannst du z.B. auf die türkische CP umschalten.

    Mehr: http://technet.microsoft.com/en-us/library/bb490874.aspx

    Im Fenstermenü von der Konsole unter Eigenschaften > Schriftart > Lucida COnsole kannst du nen vernünftigen Font einstellen.



  • @Roger Wilco! Damit dir einiges verständlicher wird, wie die Konsole bzw. Codepages arbeiten:

    #include <iostream>
    
    void printAll()
    {
    	for(int i=0; i<256; i++)
    		std::wcout << (wchar_t)i;
    	std::wcout << std::endl;
    }
    int main()
    {
    	system("chcp 850");
    	printAll();
    	system("chcp 1252"); // Mit EUR-Symbol
    	printAll();
    }
    

    Dann aber den richtigen Font einschalten!



  • Danke Artchi für Deine Hilfe, ich hab's jetzt! 😉

    Vor einiger Zeit bin ich bei der Suche nach Thema auf ein Artikel gestoßen, in dem es ein Workaround zur Widechar-Ausgabe auf die Windows-Konsole (via std::cout) gab. Dort wurde behauptet std::cout unterstütze 256 Zeichen und std::wcout nur 128.

    Der Code dort funktionierte und das mit std::wcout habe ich naiver-weise einfach geglaubt.

    Hier nochmal ein Code-Beispiel, falls hier jemand landet, der gerne Unicode-Strings (std::wstring) auf die Konsole ausgeben möchte:

    #include <windows.h> // Get/SetConsoleOutputCP()
    #include <locale>    // std::locale
    #include <string>    // std::(w)string
    #include <iostream>  // std::(w)cout
    
    int main(){
    
        unsigned int oldCodepage = GetConsoleOutputCP(); // aktuelle Konsolen-Codepage sichern
        if(!SetConsoleOutputCP(1252)){ // Codepage 1252 (Westeuropa-Zeichensatz)
          std::cout << "Failed to setup Console-Codepage!" << std::endl;
            return 1;
        }
    
        std::locale gerLocal("german"); // alternativ std::locale("") (lädt Benutzer-Einstellung)
        std::locale::global(gerLocal);
        // alternativ einzelnd setzen:
        // std::cout.imbue(gerLocal); 
        // std::wcout.imbue(gerLocal);
    
        std::cout << "std::cout: \x80" << std::endl;
        std::wcout << L"std::wcout: \x20AC" << std::endl;
    
        std::cout << "std::cout: €" << std::endl; // geht zumindest bei mir (VC6.0)
        std::wcout << L"std::wcout: €" << std::endl;
    
        SetConsoleOutputCP(oldCodepage); // ursprüngliche Codepage wieder einstellen
        return 0;
    }
    

    Konsolenausgabe:

    std::cout: €
    std::wcout: €
    std::cout: €
    std::wcout: €

    Wichtig ist nur, wie Artchi schon sagte, dass eine Unicode-Schriftart in der Konsole aktiv ist (z.B. Lucida Console):

    WINAPI schrieb:

    If the current font is a fixed-pitch Unicode font, SetConsoleOutputCP changes the mapping of the character values into the glyph set of the font, rather than loading a separate font each time it is called. This affects how extended characters (ASCII value greater than 127) are displayed in a console window. However, if the current font is a raster font, SetConsoleOutputCP does not affect how extended characters are displayed.

    Und hier noch ein empfehlenswerter Link zum Thema The Standard C++ Locale



  • Ja, das mit dem Locale und imbue() ist entscheidend (hatte ich ja mehrmals geschrieben). Leider ist es echt schwierig auf solche Infos im Web zu stossen. Die Locales und Std-Streams werden leider sehr stiefmütterlich behandelt, weil die meisten C++-User diese sehr selten benutzen.

    Das man da aber einfach "german" als Locale verwenden kann, war mir so nicht bewusst.

    Aber eine Sache ist sehr gefährlich:

    // O.K.:
        std::cout << "std::cout: \x80" << std::endl;
        std::wcout << L"std::wcout: \x20AC" << std::endl;
    
        // Gefährlich:
        std::cout << "std::cout: €" << std::endl; // geht zumindest bei mir (VC6.0)
        std::wcout << L"std::wcout: €" << std::endl;
    

    Wenn das EUR-Symbol direkt eingegeben wird, ist es Quelltext-Editor- und System-abhängig. Weil du dann das Symbol so eingibst, wie es dein Editor oder OS verstehen. Die \x-Variante ist vorzusziehen, da so eindeutig wird, welches Sonderzeichen gemeint ist.

    Danke für den Beispielcode! 👍



  • Artchi schrieb:

    Wenn das EUR-Symbol direkt eingegeben wird, ist es Quelltext-Editor- und System-abhängig. Weil du dann das Symbol so eingibst, wie es dein Editor oder OS verstehen. Die \x-Variante ist vorzusziehen, da so eindeutig wird, welches Sonderzeichen gemeint ist.

    Wann genau wird das gefährlich? Wie muss ich mir das vorstellen: OS+IDE nutzen z.B. irgendeine Codepage bei der das EUR-Zeichen den Hex-Code xyz hat und der wird dann in den String gespeichert? Ich dachte, dass wird vom Editor entsprechend umgesetzt. Immerhin unterscheidet er ja auch "€" und L"€", was unterschiedliche Hex-Codes ergibt.

    Welche Zeichen sollte man dann lieber per Hex-Code eingeben? Dann sind ja theoretisch alle Zeichen nur per Hex-Code eindeutig.



  • Wenn du dir die verschiedenen Codepages anschaust (inkl. Unicode), wirst du feststellen, das alle Zeichen bis 128 gleich sind. Und das sind konkret gesagt die ersten 128 Zeichen vom US-ASCII. Die kannst du also über das Keyboard direkt eingeben. Alles was dadrüber ist, ist in jeder Codepage potenziell anders. Das EURO-Zeichen als 129. Zeichen zählt dazu.

    Meine beiden Schleifen in dem oberen Beispiel machen das auch deutlich.


Anmelden zum Antworten