2014-01-26 2 views
12

Я пытаюсь написать строки Unicode на экран в C++ в Windows. Я изменил свой консольный шрифт для Lucida Console и я поставил вывод CP_UTF8 ака 65001.Проблемы Unicode в C++, но не C

я запускаю следующий код:

#include <stdio.h> //notice this header file.. 
#include <windows.h> 
#include <iostream> 

int main() 
{ 
    SetConsoleOutputCP(CP_UTF8); 
    const char text[] = "Россия"; 
    printf("%s\n", text); 
} 

Он печатает просто отлично!

Однако, если я:

#include <cstdio> //the C++ version of the header.. 
#include <windows.h> 
#include <iostream> 

int main() 
{ 
    SetConsoleOutputCP(CP_UTF8); 
    const char text[] = "Россия"; 
    printf("%s\n", text); 
} 

печатает: ������������

Я понятия не имею, почему ..

Другое дело, когда я делаю:

#include <windows.h> 
#include <iostream> 

int main() 
{ 
    std::uint32_t oldcodepage = GetConsoleOutputCP(); 
    SetConsoleOutputCP(CP_UTF8); 

    std::string text = u8"Россия"; 
    std::cout<<text<<"\n"; 

    SetConsoleOutputCP(oldcodepage); 
} 

Я получаю тот же результат, что и выше (нерабочий выход).

Использование printf на std::string, он отлично работает, хотя:

#include <stdio.h> 
#include <windows.h> 
#include <iostream> 

int main() 
{ 
    std::uint32_t oldcodepage = GetConsoleOutputCP(); 
    SetConsoleOutputCP(CP_UTF8); 

    std::string text = u8"Россия"; 
    printf("%s\n", text.c_str()); 

    SetConsoleOutputCP(oldcodepage); 
} 

, но только если я использую stdio.h и НЕ cstdio.

Любые идеи, как я могу использовать std::cout? Как я могу использовать cstdio? Почему это происходит? Это не cstdio только версия C++ stdio.h?

EDIT: Я просто попытался:

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

int main() 
{ 
    _setmode(_fileno(stdout), _O_U8TEXT); 
    std::wcout << L"Россия" << std::endl; 
} 

и да, это работает, но только если я использую std::wcout и wide strings. Я действительно хотел бы избежать wide-strings и единственное решение, которое я вижу до сих пор является C-printf: л

Так что вопрос все еще стоит ..

+1

Что делать, если вы используете 'std :: printf', когда включаете' cstdio'? –

+1

Он печатает те же плохие символы. Никакой разницы с или без 'std ::' я использую Mingw 4.8.1. Самая последняя сборка. – Brandon

+0

Что говорит '' говорят, что это выводит? –

ответ

2

Хотя вы установили консоль ожидать вывода UTF-8, I что ваш компилятор обрабатывает строковые литералы как находящиеся в каком-то другом наборе символов. Я не знаю, почему компилятор C работает по-другому.

Хорошей новостью является то, что C++ 11 включает некоторую поддержку UTF-8 и что Microsoft реализовала соответствующие части Стандарта. Код немного волосатый, но вы хотите посмотреть на std::wstring_convert (конвертирует в и из UTF-8) и заголовок <cuchar>.

Вы можете использовать эти функции для преобразования в UTF-8, и если ваша консоль ожидает UTF-8, все должно работать правильно.

Лично, когда мне нужно отлаживать что-то вроде этого, я часто направляю вывод в текстовый файл. Текстовые редакторы, похоже, работают с Unicode лучше, чем консоль Windows. В моем случае, я часто выводят коды правильно, но консоль настроена неправильно, так что я все еще заканчиваю печать мусора.


Я могу вам сказать, что это работает для меня как в Linux (с использованием Clang) и Windows (с помощью GCC 4.7.3 и Clang 3.5, вам нужно добавить «станд = C++ 11» в команду линия для компиляции с GCC или Clang):

#include <cstdio> 

int main() 
{ 
    const char text[] = u8"Россия"; 
    std::printf("%s\n", text); 
} 

использование Visual C++ (2012, но я считаю, что это также будет работать с 2010 года), я должен был использовать:

#include <codecvt> 
#include <cstdio> 
#include <locale> 
#include <string> 

int main() 
{ 
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; 
    auto text = converter.to_bytes(L"Россия"); 
    std::printf("%s\n", text.c_str()); 
} 
-2

это более удивительно, что C осуществление делает работа здесь, чем C++. char может содержать только один байт (числовые значения 0-255), и, таким образом, консоль должна отображать только символы ASCII.

C должен делать некоторые магии для вас здесь - на самом деле это предполагает, что эти байты из вне диапазона ASCII (который 0-127) вы предоставляете образуют в Unicode (вероятно, UTF-8) мульти- байтовый символ. C++ просто отображает каждый байт вашего массива const char[], и поскольку байты UTF, обработанные отдельно, не имеют отдельных символов в вашем шрифте, они помещают эти . Обратите внимание, что вы назначаете 6 букв и получаете 12 вопросительных знаков.

Вы можете прочитать о UTF-8 и ASCII кодировку, если вы хотите, но суть в том, что std::wstring и std::wcout действительно лучшее решение, предназначенное для обработки больших, чем байт символов.

(Если вы не используете латинские символы вообще, вы не даже экономии памяти при использовании char -На решения, такие как const char[] и std::string вместо std::wstring. Все эти кириллические коды должны занять некоторое пространство в любом случае) ,

+0

Если я использую 'std :: wstring' и' std :: wcout', он ничего не печатает .. ничего вообще. На самом деле это было первое, что я пробовал. Я также был удивлен, что C-код работал, но не C++-код. Я пробовал все для gcc/g ++, включая 'setlocale (LC_ALL,« Russian »)' и 'system (« chcp 65001> 0 »);'. Все. Единственными решениями, которые работали, были C один и '_setmode', и они были на OP. Ничто другое не работает/не работает. Даже C++ 'printf'. – Brandon

+1

Но в вашем вопросе вы цитируете фрагмент с 'std :: wcout' и говорите, что это работает ... –

+0

То есть, если я использую' _setmode'. Использование 'std :: wcout' с' SetConsoleOutputCP' не работает. – Brandon

1

Если файл кодируется как UTF-8, вы увидите, длина строки 12. Выполнить strlen из <string.h> (<cstring>) на нем, чтобы увидеть, что я имею в виду. Установка выходной кодовой страницы будет печатать байты точно так, как вы их видите.

Что видит компилятор эквивалентно следующему:

const char text[] = "\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd1\x8f"; 

Заверните его в широкой строке (wchar_t в частности), и все не так хорошо.

Почему C++ обрабатывает его по-разному? Я не имею ни малейшего понятия, кроме, возможно, что механизм, используемый кодом, лежащим в основе версии C++, несколько неосведомлен (например, std::cout счастливо выводит все, что вы хотите вслепую). Какая бы ни была причина, по-видимому, придерживаться C, это безопаснее ... что на самом деле неожиданно для меня, учитывая тот факт, что собственный C-компилятор C не может даже скомпилировать код C99.

В любом случае, я бы посоветовал не выводить на консоль Windows, если это возможно, Unicode или нет. Файлы настолько надежнее, не говоря уже о проблемах.