UTF-8 Strings und Sonderzeichen Visual Studio 2015



  • Hallo Leute,

    ich habe ein Problem mit UTF-8 Strings. Ich benutze Microsoft Visual Studio 2015 mit dem standart Compiler. Ich habe nun eine UTF-8 String, welcher ein Sonderzeichen ('@') ausgeben soll

    #include <iostream>
    [...]
    
    int main()
    {
    std::cout << u8"\u00a9 [...]" << std::endl;
    return 0;
    }
    

    Jedoch bekomme ich als Output in der Konsole: ┬®
    Ich habe mich natürlich bereits auf StackOverflow und co. informiert, jedoch habe ich keinerlei Hinweis auf einen möglichen Fehler gefunden.
    Eine Idee die ich für plausibel halte könnte sein, dass es möglicherweise am Compiler liegt, jedoch bezweifle ich wiederum, dass so etwas nicht einmal Microsoft passieren würde.

    Vielleicht kennt jemand ja die Lösung für mein Problem.

    crsf1re



  • Hi,

    das liegt nicht an VS 2015, sondern an Windows. Windows benutzt Standard mäßig nicht utf-8. Du musst im Prinzip der Konsole sagen, wie die Ausgabe zu interpretieren ist.

    Schau mal hier: https://stackoverflow.com/questions/10882277/properly-print-utf8-characters-in-windows-console als Startpunkt.

    Stichwörter sind noch Utf16 und Widestring. Wenn ich morgen wieder am PC sitze kann ich mal raus suchen, wie ich ein ähnliches Problem letztens gelöst habe.

    Edit: Tags repariert.



  • Abgesehen davon ist U+00A9 das Copyright Zeichen und nicht '@'. '@' ist in ASCII enthalten und braucht auch in UTF-8 nur ein Byte.



  • Ich habe mich mal zu einem Doppelpost entschlossen, da für ein Edit es doch etwas zu viel ist.

    1. \u00a9 ist werder UTF-8 noch das @ Zeichen. Das ist die Unicode Kodierung von © (Copyright Symbol)
    In utf-8 wird das mit zwei Byte dargestellt. Das Copyright Symbol wäre dann c2 a9.
    Aber Achtung, die Schriftart der Windowskonsole kann das nicht darstellen... aber wenn du das in eine Datei schreibst klappt das.
    Das UTF-8 Zeichen für @ ist: 40.

    2. Ausgeben kann man das über std::locale und std::codevect. Hier mal mein COde dafür:

    #include <iostream>
    #include <locale>
    #include <codecvt>
    #include <fstream>
    
    int main() {
    //  Foo foo;
      std::locale loc(std::locale(), new std::codecvt_utf16<wchar_t>);
      std::wcout << L"\x40 [...]" << std::endl; //@ Zeichen
      std::wcout << L"\xc2\xa9  [...]" << std::endl; //Copyright Zeichen, wird auf der Konsole falsch dargestellt.
      std::wofstream test(L"test.txt", std::wofstream::out);
      test << L"\xc2\xa9 [...]" << std::endl; //In der Datei wird dann auch das Copyright Zeichen richtig dargestellt.
      std::cin.ignore();
    }
    

    Ich habe mal am Rande gehört, dass <codecvt> deprecated werden soll. Ich habe dafür aber grade auf die Schnelle keine Quelle gefunden, daher kann man das erstmal weiter verwenden würde ich sagen.

    Edit: zu langsam...



  • Ich habs noch nicht ausprobiert, aber kann man eventuell mit der Methode und stattdessen std::codecvt_utf8_utf16 auch das std::cout -Objekt zur Ausgabe verwenden?
    Ich persönlich fände das vorteilhafter für portablen Code, der dem "Überall UTF-8"-Paradigma folgt. So müsste man unter Windows nur einmal die Konvertierungs-Facette
    aktivieren und kann dann überall im Code via std::cout UTF-8 ausgeben anstatt alles mit std::wcout zuzupflastern, was das z.B. unter Linux wieder problematisch sein könnte.

    Ansonsten funktionert die direkte UTF-8-Ausgabe in der Windows-Konsole durchaus, wenn man sie mit SetConsoleOutputCP(CP_UTF8) aktiviert, aber meiner Erfahrung nicht über
    [c]std::cout[/c], sondern nur mit lowlevel-Textausgabe wie [c]fprintf[/c] Auf Standardausgabe-Dateihandle, [c]write(stdout, ...)[/c] oder [c]WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), ...)[/c].
    Warum das so ist, kann ich leider nicht genau sagen, da mir die Innereien der C++-Streams auch nach vielen Jahren immer noch ein Mysterium sind 😉

    Update: Gerade nochmal etwas rumgespielt mit SetConsoleOutputCP(CP_UTF8) und std::cout in VS2015. Ob das funktioniert scheint wohl davon abzuhängen, ob man die Datei als
    UTF-8 mit oder ohne BOM speichert und ob man für das String-Literal ein u8 -Präfix verwendet. Das kommt mir irgendwie ein wenig inkonsistent vor, was der Compiler da veranstaltet:

    #include <iostream>
    #ifdef _WIN32
         #include <Windows.h>
    #endif
    
    auto main() -> int
    {
        #ifdef _WIN32
            SetConsoleOutputCP(CP_UTF8);
        #endif
        std::cout << u8"A_©öäüß_Z" << std::endl;
        return 0;
    }
    
    Codierung der Datei in VS2015                            String-Literal         Ausgabe
    
    Unicode (UTF-8 with signature) - Codepage 65001          u8"A_©öäüß_Z"          A_©öäüß_Z
    Unicode (UTF-8 with signature) - Codepage 65001          "A_©öäüß_Z"            A__Z
    Unicode (UTF-8 without signature) - Codepage 65001       u8"A_©öäüß_Z"          A_©öäüß_Z
    Unicode (UTF-8 without signature) - Codepage 65001       "A_©öäüß_Z"            A_©öäüß_Z
    

    Sieht mir aus als würd sich MSVC da ganz schön verhaspeln. Nach meinem Verständnis sollte er eigentlich in allen 4 Fällen dasselbe ausgeben.

    P.S.: Bin selbst Fan der letzten Variante: .cpp-Datei in UTF-8 ohne BOM und kein String-Präfix - UTF-8 überall und für alles 😉



  • Vielen Danke für die schnellen und ausführlichen Antworten.

    Ich habe jetzt @Finnegan 's Vorgehensweise ausprobiert und sie scheint auch bei mir (zu mindest in den meisten Fällen) zu funktionieren.

    Und ich entschuldige mich im Nachhinein für meinen groben Fehler bei der Angabe des @ Unicodes 😃



  • crsf1re schrieb:

    Ich habe jetzt @Finnegan 's Vorgehensweise ausprobiert und sie scheint auch bei mir (zu mindest in den meisten Fällen) zu funktionieren.

    Falls es dein erstes Mal sein sollte: Immer schön isolieren das Einbinden Windows.h , z.B. in einer separaten .cpp-Datei. Die Windows-Header gehören
    mit zu den schlimmsten Header-Seuchen die man sich einfangen kann: Jede Menge Makros ohne Präfix mit Allerwelts-Namen die man oft gern selbst
    verwenden würde ( min, max, far, near, ERROR, etc.). Kann teilweise zu ziemlich ekligen Fehlermeldungen führen (Klassiker: wenn man irgendwann
    mal std::min/std::max verwendet. #define NOMINMAX hilft, aber es gibt noch andere Klöpse).



  • Wie sieht das eigentlich aus, wenn man in eine Datei schreiben wollen würde (unter Windows)?

    Wenn ich das mit std::locale und std::codecvt mache, habe ich da keine Probleme. Auch wenn die Wide String Geschichte für Crossplattform nicht geeignet ist.

    Mit SetConsoleOutputCP hat doch nur Auswirkung auf die Code Page die von der Konsole genutzt wird, oder?


Anmelden zum Antworten