Konsole und UTF8
-
Hi!
UTF8-Ausgabe auf der Konsole will bei mir einfach nicht klappen.. Mein Testcode:
// UTF-8-Codepage setzen if ( ! SetConsoleOutputCP( 65001 ) ) MessageBox( 0, 0, 0, 0 ); char s[] = { 0xC3, 0x84 }; // Zum Testen 'Ä', sollte in der Font vorhanden sein. DWORD d; if ( ! WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), s, 2, &d, 0 ) ) MessageBox( 0, 0, 0, 0 );Beide Funktionen schlagen nicht fehl, Codepage 65001 ist auch in der Registry angegeben. Lustigerweise erscheint nicht nur das Ä nicht, bei jedem Durchlauf erscheinen andere Buchstaben/Zeichen, aber immer zwei.
Weiß jemand, warum das nicht funktioniert? Schließlich scheint der Konsole der Umschwung auf UTF8 nichts auszumachen, dann erwarte ich auch von ihr, dass sie UTF8 anzeigt.
edit: Alrighty, hiermit funktioniert's: http://blog.kalmbachnet.de/?postid=98. Trotzdem frage ich mich, warum obiges nicht hinhaut..?
-
Windows ist UTF16 basierend und nicht UTF8!
Du kannst kein UTF8 ausgeben; das unterstützt Windows nicht.
Auch verwendest Du die W-Funktion die sowieso nur TUF16 entgegennimmt und somit Dein Ansatz sowieso ganz falsch war....Was (theoretisch) gehen würde ist, dass Du die A-Funktionen verwendest und vorher die lokale Codpage auf UTF8 setzt. Aber wie gesagt: Das geht nur Theoretisch da Windows UTF8 als lokale Codepage nicht unterstützt (Michael Kaplan hat darüber auch schon mal irgendwann gebloggt).
-
Ok, gut, danke! Was ich aber dann nicht verstehe: Weißt du zufällig, warum hier (http://msdn.microsoft.com/en-us/library/ms776446(VS.85).aspx) steht, dass die UTF-16-Codepage nur von managed Anwendungen unterstützt wird ("utf-16 [...] available only to managed applications")? Nur deshalb wollte ich's überhaupt mit UTF-8 versuchen, dachte ich doch, dass UTF-16 nicht funktionieren würde.
Ich probier nochmal ne Weile herum, danke soweit
PS: Die W-Funktion zu nehmen, war wirklich dämlich

PPS: Jo, ist ja lächerlich, mit WriteConsoleW und UTF16-Strings funktioniert ja alles tadellos

-
Es ist mir ja fast schon peinlich, aber ich krieg's mit std::wcout nicht hin:
std::wcout << L"..Ö.." << std::endl; // Zeigt "..Î.." an std::wcout << L"..\u2261.." << std::endl; // Zeigt ".." an, Ausgabe scheint dann abzubrechen, sonst kein sichtbarer FehlerDie Konsole ist aber definitiv Unicode-fähig, denn mit
DWORD d; WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), L"..Ö..\n", 6, &d, 0 ); WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), L"..\u2261..\n", 6, &d, 0 );klappt's ja einwandfrei. Ich hab eine Weile nach 'codecvt's und 'locale's gegoogelt, aber nichts für mich gefunden (anscheinend wollen alle nur mit UTF-8 arbeiten).
Aso, falls es eine Rolle spielen sollte: Ich nutze z.Zt. VC++ Express 2005 auf Vista 64.
PS: 0x2261 ist ≡ (Link).
-
Aber mein Blog hast Du aufmerksam gelesen

http://blog.kalmbachnet.de/?postid=23
Also one problem is that MS is not able to provide the full UNICODE glyphs to the default console font! If you want to display UNICODE you must use WriteConsoleW and you must change the font to “Lucida Console”.
-
Gilt das auch noch für VC++ 2008? Oder ist das unabhängig von der verwendeten CRT?
-
Das hat ja nix mit VS zu tun, sondern mit dem OS. Vielleicht mag dies in Vista anders sein, hab ich aber noch nicht probiert; kann es mir aber nicht vorstellen.
-
Jochen Kalmbach schrieb:
Aber mein Blog hast Du aufmerksam gelesen

Ich hab nicht alles gelesen, das geb ich zu :p Ist aber voll mit wertvollem Wissen dein Blog, finde ich immer wieder erstaunlich

Also one problem is that MS is not able to provide the full UNICODE glyphs to the default console font! If you want to display UNICODE you must use WriteConsoleW and you must change the font to “Lucida Console”.
Die Glyphen werden bei WriteConsoleW korrekt angezeigt, deshalb finde ich's so schade, dass wcout da versagt.
Ich kann das aber irgendwie nicht nachvollziehen: wstring und wcout sind für Unicode-Zeichen gedacht und "unterstützen" UCS2. Wieso wird dann intern Multibyte statt UCS2 verwendet? Ist ja einfach schauderhaft, die Implementierung

Gibt's da eine Lösung? Ich probiere mal, den internen Puffer auszutauschen und eine eigene Locale reinzuhauen.
-
Funzt

#ifdef _WIN32 #include <windows.h> #include <iostream> namespace WindowsConsoleFix { // Neuer Streambuf, der korrekt in die Konsole schreibt class MyStream : public std::wstreambuf { using std::wstreambuf::int_type; using std::wstreambuf::traits_type; protected: std::streamsize xsputn( const wchar_t* p, std::streamsize count ) { DWORD d; WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), p, count, &d, 0 ); return count; } int_type overflow( int_type c=traits_type::eof() ) { if ( c != traits_type::eof() ) { DWORD d; WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &d, 0 ); } return c; } }; // Dummy-Struct, deren Konstruktor zu Programmstart aufgerufen wird struct WinConsoleFix { WinConsoleFix() { // Nur wenn stdout auch mit einer Konsole verbunden ist, // soll das neue Stream-Objekt drangehangen werden. if ( isConsoleAttached() ) std::wcout.rdbuf( &console_stream ); } bool isConsoleAttached() { // Hier wird quasi nur getestet, ob das stdout-Handle auf eine Konsole verweist. COORD c = GetLargestConsoleWindowSize( GetStdHandle(STD_OUTPUT_HANDLE) ); return c.X!=0 && c.Y!=0; } MyStream console_stream; }; WinConsoleFix fix; } #endifedit: Nur std::endl kann ich noch nicht abfangen..
edit2: std::endl funktioniert jetzt auch.
edit3: Wem die Benutzung nicht klar ist: Einfach das Obige in eine eigene cpp-Datei werfen. Mehr brauchts nicht, auch nichts irgendwo inkludieren oder so.
-
Geht es auch nicht, wenn Du es zuvor explizit aktiviert hast? Ich dachte, dann geht es.... (_setmode)
-
Jochen Kalmbach schrieb:
Geht es auch nicht, wenn Du es zuvor explizit aktiviert hast? Ich dachte, dann geht es.... (_setmode)
Ne, irgendwie nicht

Aber obiger Codefetzen funktioniert, das reicht mir erstmal. std::endl hab ich auch gerade hingekriegt und oben korrigiert. Nicht, dass ich ganz verstanden hätte, was ich da getan hab, aber solange es funktioniert, will ich mich nicht beschweren
