Проблемы с Unicode в С++, но не C
Я пытаюсь написать строки unicode на экране в С++ в Windows. Я изменил свой консольный шрифт на Lucida Console
, и я установил вывод на CP_UTF8
aka 65001.
Я запускаю следующий код:
#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
и NOT cstdio
.
Любые идеи, как я могу использовать std::cout
? Как я могу использовать cstdio
?
Почему это происходит? Не cstdio
просто версия С++ 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
: l
Итак, вопрос все еще стоит.
Ответы
Ответ 1
Хотя вы настроили консоль на вывод UTF-8, я подозреваю, что ваш компилятор обрабатывает строковые литералы как находящиеся в каком-то другом наборе символов. Я не знаю, почему компилятор 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, вам нужно добавить "std = С++ 11" в командную строку для компиляции с помощью GCC или Clang):
#include <cstdio>
int main()
{
const char text[] = u8"Россия";
std::printf("%s\n", text);
}
Использование Visual С++ (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
Если ваш файл закодирован как UTF-8, вы найдете длину строки 12. Запустите strlen
от <string.h>
(<cstring>
) на нем, чтобы понять, что я имею в виду. Установка выходной кодовой страницы будет печатать байты точно так, как вы их видите.
То, что видит компилятор, эквивалентно следующему:
const char text[] = "\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd1\x8f";
Оберните его в широкую строку (в частности, wchar_t
), и все будет не так хорошо.
Почему С++ обрабатывает его по-другому? Я не имею ни малейшего понятия, кроме, может быть, механизм, используемый кодом, лежащим в основе версии С++, несколько неосведомлен (например, std::cout
счастливо выводит все, что вы хотите вслепую). Какая бы ни была причина, по-видимому, придерживаться C, является самым безопасным... что на самом деле неожиданно для меня, учитывая тот факт, что собственный C-компилятор C не может даже скомпилировать код C99.
В любом случае, я бы посоветовал не выводить на консоль Windows, если это возможно, Unicode или нет. Файлы настолько надежнее, не говоря уже о трудностях.
Ответ 3
Более удивительно, что реализация C работает здесь, а не С++. char
может содержать только один байт (числовые значения 0-255), и, таким образом, консоль должна отображать только символы ASCII.
C должен делать для вас магию - на самом деле он полагает, что эти байты вне диапазона ASCII (который 0-127) вы предоставляете из многобайтового символа Unicode (возможно, UTF-8). С++ просто отображает каждый байт вашего массива const char[]
, и поскольку байты UTF, обработанные отдельно, не имеют отдельных символов в вашем шрифте, он помещает эти. Обратите внимание, что вы назначаете 6 букв и получаете 12 вопросительных знаков.
Вы можете прочитать UTF-8 и ASCII, если вы хотите, но дело в том, что std::wstring
и std::wcout
- действительно лучшее решение, предназначенное для обработки символов большего размера.
(Если вы вообще не используете латинские символы, вы даже не сохраняете память, когда используете char
-решенные решения, такие как const char[]
и std::string
вместо std::wstring
. Все эти кириллические коды в любом случае, должны занимать некоторое пространство).