2013-10-05 2 views
10

Рассмотрим следующий фрагмент кода, скомпилированного в качестве консольного приложения на MS Visual Studio 2010/2012 и выполняется на Win7:Почему некоторые символы Unicode приводят к отказу std :: wcout в консольном приложении?

#include "stdafx.h" 
#include <iostream> 
#include <string> 


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; 
} 

Первые выходы wcout заявление «привет» (вместо того, чтобы что-то вроде «привет тест?! ") Второй вывод wcout ничего не выводит.

Это как если бы 0xf021 (и другие?) Символы Unicode приводили к сбою wcout.

Этот символ Юникода, 0xf021 (кодированный как UTF-16), является частью «Частной области использования» на базовой многоязычной плоскости. Я заметил, что приложения Windows Console не имеют расширенной поддержки символов Unicode, но обычно каждый символ, по крайней мере, представлен символом по умолчанию (например, «?»), Даже если нет поддержки для отображения определенного глифа.

Что заставляет поток наружного воздуха задыхаться? Есть ли способ сбросить его после входа в это состояние?

ответ

13

wcout, или, если быть точным, экземпляр wfilebuf, который он использует внутри, преобразует широкие символы в узкие символы, а затем записывает их в файл (в вашем случае - stdout). Преобразование выполняется фасет codecvt в локали потока; по умолчанию это просто wctomb_s, конвертируя в кодовую страницу ANSI по умолчанию, а также CP_ACP.

Видимо, символ '\xf021' не может быть указан в кодовой странице по умолчанию, настроенной в вашей системе. Таким образом, преобразование завершается неудачно, и в потоке установлен failbit. После установки failbit все последующие вызовы немедленно завершаются.

Я не знаю, как получить wcout, чтобы успешно печатать произвольные символы Юникода для консоли. wprintf работает, хотя, с немного подправить:

#include <fcntl.h> 
#include <io.h> 
#include <string> 

const std::wstring test = L"hello\xf021test!"; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    _setmode(_fileno(stdout), _O_U16TEXT); 
    wprintf(test.c_str()); 

    return 0; 
} 
+0

Вы можете попробовать 'imbue'ing поток с utf16 фаской. Не знаю, если это действительно сработает. –

+2

Пробовал это, не смог заставить его работать. 'wcout' использует фасет для преобразования широкой строки в последовательность байтов (что для' codecvt_utf16' в основном не-op), а затем записывает их с помощью 'fwrite' по одному байту за раз, по причинам, которые меня избегают. Без '_setmode' вы получаете по одному символу для каждого байта, включая глиф« неизвестный символ »для нулей. С '_setmode' вы получаете подтверждение внутри' fwrite', жалуясь на то, что его попросят написать нечетное количество байтов. –

+0

Крысы. Спасибо за попытку. –

10

Установку режима для стандартного вывода в _O_U16TEXT позволит писать символы Unicode в поток wcout, а также wprintf. (См. Conventional wisdom is retarded, aka What the @#%&* is _O_U16TEXT?) Это right way, чтобы выполнить эту работу.

_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; 

Это не должно быть необходимости больше, но вы можете сбросить поток, который вступил в состояние ошибки, вызвав ясно:

if (std::wcout.fail()) 
{ 
    std::wcout.clear(); 
} 
Смежные вопросы