Perché alcuni caratteri Unicode causano il fallimento di std :: wcout in un’app console?

Considera il seguente frammento di codice, compilato come applicazione console su MS Visual Studio 2010/2012 ed eseguito su Win7:

#include "stdafx.h" #include  #include  const std::wstring test = L"hello\xf021test!"; int _tmain(int argc, _TCHAR* argv[]) { std::wcout << test << std::endl; std::wcout << L"This doesn't print either" << std::endl; return 0; } 

La prima istruzione wcout emette “ciao” (invece di qualcosa come “ciao? Prova!”). La seconda istruzione wcout non produce nulla.

È come se 0xf021 (e altri?) Caratteri Unicode causino il fallimento di wcout.

Questo particolare carattere Unicode, 0xf021 (codificato come UTF-16), fa parte della “Area di utilizzo privata” nel piano multilingue multilingue. Ho notato che le applicazioni di Windows Console non hanno un ampio supporto per i caratteri Unicode, ma in genere ogni personaggio è rappresentato almeno da un carattere predefinito (ad esempio “?”), Anche se non c’è supporto per il rendering di un glifo particolare.

Cosa sta causando il soffocamento del stream di wcout? C’è un modo per resettarlo dopo che è entrato in questo stato?

wcout , o per essere precisi, un’istanza wfilebuf che usa internamente, converte caratteri ampi in caratteri stretti, quindi li scrive nel file (nel tuo caso, in stdout ). La conversione viene eseguita dal facet codecvt nella locale del stream; per impostazione predefinita, ciò fa semplicemente wctomb_s , convertendosi alla codepage ANSI predefinita del sistema, ovvero CP_ACP .

Apparentemente, il carattere '\xf021' non è rappresentabile nella codepage predefinita configurata sul sistema. Quindi la conversione fallisce e failbit è impostato nello stream. Una volta impostato failbit , tutte le chiamate successive falliscono immediatamente.

Non conosco alcun modo per far wcout che wcout stampi arbitrariamente caratteri Unicode arbitrari su console. wprintf funziona però, con un piccolo ritouch:

 #include  #include  #include  const std::wstring test = L"hello\xf021test!"; int _tmain(int argc, _TCHAR* argv[]) { _setmode(_fileno(stdout), _O_U16TEXT); wprintf(test.c_str()); return 0; } 

L’impostazione della modalità per lo stdout su _O_U16TEXT ti consentirà di scrivere caratteri Unicode sul stream di wcout e su wprintf. (Vedi la saggezza convenzionale è ritardata, alias What the @ #% & * is _O_U16TEXT? ) Questo è il modo giusto per farlo funzionare.

 _setmode(_fileno(stdout), _O_U16TEXT); std::wcout << L"hello\xf021test!" << std::endl; std::wcout << L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd" << std::endl; std::wcout << L"Now this prints!" << std::endl; 

Non dovrebbe essere più necessario ma puoi reimpostare un stream che è entrato in uno stato di errore chiamando clear:

 if (std::wcout.fail()) { std::wcout.clear(); }