Вывод строк unicode в консольном приложении Windows

Привет, я пытался вывести строку unicode на консоль с помощью iostreams и не удалось.

Я нашел это: Использование шрифта unicode в консольном приложении С++, и этот фрагмент работает.

SetConsoleOutputCP(CP_UTF8);
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize]; 
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m);

Однако, я не нашел способа правильно выводить юникод с помощью iostreams. Любые предложения?

Это не работает:

SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl;

ИЗМЕНИТЬ Я не нашел другого решения, кроме как обернуть этот фрагмент в поток. Надеюсь, у кого-то есть лучшие идеи.

//Unicode output for a Windows console 
ostream &operator-(ostream &stream, const wchar_t *s) 
{ 
    int bufSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
    char *buf = new char[bufSize];
    WideCharToMultiByte(CP_UTF8, 0, s, -1, buf, bufSize, NULL, NULL);
    wprintf(L"%S", buf);
    delete[] buf; 
    return stream; 
} 

ostream &operator-(ostream &stream, const wstring &s) 
{ 
    stream - s.c_str();
    return stream; 
} 

Ответы

Ответ 1

Я проверил решение здесь, используя Visual Studio 2010. Через эту статью MSDN и сообщение в блоге MSDN. Трюк - это неясный вызов _setmode(..., _O_U16TEXT).

Решение:

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

int wmain(int argc, wchar_t* argv[])
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    std::wcout << L"Testing unicode -- English -- Ελληνικά -- Español." << std::endl;
}

Скриншот:

Unicode in console

Ответ 2

У неё должен быть установлен языковой стандарт по-разному с ЭЛТ. Вот как это можно исправить:

int _tmain(int argc, _TCHAR* argv[])
{
    char* locale = setlocale(LC_ALL, "English"); // Get the CRT current locale.
    std::locale lollocale(locale);
    setlocale(LC_ALL, locale); // Restore the CRT.
    std::wcout.imbue(lollocale); // Now set the std::wcout to have the locale that we got from the CRT.
    std::wcout << L"¡Hola!";
    std::cin.get();
    return 0;
}

Я только что протестировал его, и он отображает строку здесь совершенно нормально.

Ответ 3

SetConsoleCP() и chcp не то же самое!

Возьмите этот фрагмент программы:

SetConsoleCP(65001)  // 65001 = UTF-8
static const char s[]="tränenüberströmt™\n";
DWORD slen=lstrlen(s);
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),s,slen,&slen,NULL);

Исходный код должен быть сохранен как спецификация UTF-8 без (знак порядка байтов, подпись). Затем компилятор Microsoft cl.exe берет строки UTF-8 как есть.
Если этот код сохранен с спецификацией, cl.exe транскодирует строку в ANSI (то есть CP1252), которая не соответствует CP65001 (= UTF-8).

Измените шрифт дисплея на Консоль Lucidia, иначе выход UTF-8 не будет работать вообще.

  • Тип: chcp
  • Ответ: 850
  • Тип: test.exe
  • Ответ: tr├ñnen├╝berstr├ÂmtÔäó
  • Тип: chcp
  • Ответ: 65001 - этот параметр изменился на SetConsoleCP(), но без полезного эффекта.
  • Тип: chcp 65001
  • Тип: test.exe
  • Ответ: tränenüberströmt™ - теперь все ОК.

Протестировано: немецкий Windows XP SP3

Ответ 4

Unicode Hello World на Китайский

Вот Hello World на китайском языке. На самом деле это просто "Привет". Я тестировал это на Windows 10, но я думаю, что он может работать с Windows Vista. Перед Windows Vista это будет сложно, если вы хотите программное решение, вместо настройки консоли/реестра и т.д. Возможно, посмотрите здесь, если вам действительно нужно сделать это в Windows 7: Изменить консоль Font Windows 7

Я не хочу утверждать, что это единственное решение, но это то, что сработало для меня.

Контур

  1. Настройка проекта Unicode
  2. Установите кодовую страницу консоли в unicode
  3. Найдите и используйте шрифт, который поддерживает символы, которые вы хотите отобразить
  4. Используйте язык языка, который хотите отобразить.
  5. Используйте широкоэкранный вывод, т. std::wcout

1 Настройка проекта

Я использую Visual Studio 2017 CE. Я создал пустую консольную программу. Настройки по умолчанию в порядке. Но если у вас возникнут проблемы или вы используете другой идеал, вы можете проверить их:

В свойствах проекта найдите свойства конфигурации → Общие → Стандартные по умолчанию → Набор символов. Это должно быть "Использовать набор символов Unicode", а не "Multi-Byte". Это определит _UNICODE и UNICODE для вас.

int wmain(int argc, wchar_t* argv[])

Также я думаю, что мы должны использовать функцию wmain вместо main. Они оба работают, но в среде unicode wmain может быть более удобным.

Кроме того, мои исходные файлы кодируются в кодировке UTF-16-LE, которая по умолчанию используется в Visual Studio 2017.

2. Кодовая страница консоли

Это совершенно очевидно. Нам нужна кодировка юникода в консоли. Если вы хотите проверить кодовую страницу по умолчанию, просто откройте консоль и введите chcp с любыми аргументами. Мы должны изменить его на 65001, что является кодировкой UTF-8. Идентификаторы Windows Codepage Для этой кодовой страницы есть макрос препроцессора: CP_UTF8. Мне нужно было установить и кодовую страницу ввода и вывода. Когда я опустил один, вывод был неверным.

SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

Вы также можете проверить возвращаемые значения boolean для этих функций.

3. Выберите шрифт

До сих пор я не нашел шрифт консоли, который поддерживает каждый символ. Поэтому я должен был выбрать один. Если вы хотите выводить символы, которые частично доступны только в одном шрифте и частично в другом шрифте, то я считаю, что найти решение невозможно. Только возможно, если есть шрифт, который поддерживает каждый символ. Но также я не смотрел, как установить шрифт.

Я думаю, что невозможно использовать два разных шрифта в одном окне консоли одновременно.

Как найти совместимый шрифт? Откройте консоль, перейдите к свойствам окна консоли, щелкнув значок в верхнем левом углу окна. Перейдите на вкладку "Шрифты" и выберите шрифт и нажмите "ОК". Затем попробуйте ввести символы в окне консоли. Повторяйте это, пока не найдете шрифт, с которым вы можете работать. Затем запишите имя шрифта.

Также вы можете изменить размер шрифта в окне свойств. Если вы нашли нужный размер, обратите внимание на значения размера, которые отображаются в окне свойств в разделе "выбранный шрифт". Он будет показывать ширину и высоту в пикселях.

Чтобы программно установить шрифт, вы используете:

CONSOLE_FONT_INFOEX fontInfo;
// ... configure fontInfo
SetCurrentConsoleFontEx(hConsole, false, &fontInfo);

См. Мой пример в конце этого ответа. Или посмотрите его в прекрасном руководстве: SetCurrentConsoleFont. Эта функция существует только с Windows Vista.

4. Установите языковой стандарт

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

char* a = setlocale(LC_ALL, "chinese");

Возвращаемое значение интересно. Он будет содержать строку для описания того, какой язык был выбран. Просто дайте ему попробовать :-) Я протестировал с chinese и german. Больше информации: setlocale

5. Используйте широкоэкранный вывод

Здесь нечего сказать. Если вы хотите выводить широкие символы, используйте это, например:

std::wcout << L"你好" << std::endl;

О, и не забудьте префикс L для широких символов! И если вы введете буквенные символы Unicode, подобные этому в исходном файле, исходный файл должен быть закодирован в кодировке Юникод. Подобно стандарту в Visual Studio является UTF-16-LE. Или, возможно, используйте notepad++ и установите кодировку в UCS-2 LE BOM.

пример

Наконец, я привел все это в качестве примера:

#include <Windows.h>
#include <iostream>
#include <io.h>
#include <fcntl.h>
#include <locale.h>
#include <wincon.h>

int wmain(int argc, wchar_t* argv[])
{
    SetConsoleTitle(L"My Console Window - 你好");
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    char* a = setlocale(LC_ALL, "chinese");
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);

    CONSOLE_FONT_INFOEX fontInfo;
    fontInfo.cbSize = sizeof(fontInfo);
    fontInfo.FontFamily = 54;
    fontInfo.FontWeight = 400;
    fontInfo.nFont = 0;
    const wchar_t myFont[] = L"KaiTi";
    fontInfo.dwFontSize = { 18, 41 };
    std::copy(myFont, myFont + (sizeof(myFont) / sizeof(wchar_t)), fontInfo.FaceName);

    SetCurrentConsoleFontEx(hConsole, false, &fontInfo);

    std::wcout << L"Hello World!" << std::endl;
    std::wcout << L"你好!" << std::endl;
    return 0;
}

Приветствия!

Ответ 5

Я не думаю, что есть простой ответ. глядя на Консольные кодовые страницы и Функция SetConsoleCP кажется что вам нужно будет настроить соответствующую кодовую страницу для набора символов, который вы собираетесь вывести.

Ответ 6

Повторяю, я хотел передать unicode из Python в консоль Windows, и вот минимальный размер, который мне нужно сделать:

  • Вы должны установить шрифт консоли для одного символа юникода. Существует не широкий выбор: Свойства консоли > Шрифт > Консоль Lucida
  • Вы должны изменить текущую кодовую страницу консоли: запустите chcp 65001 в консоли или используйте соответствующий метод в коде на С++
  • записать в консоль с помощью WriteConsoleW

Посмотрите интересную статью о java unicode на консоли Windows

Кроме того, в Python вы не можете писать по умолчанию sys.stdout в этом случае, вам нужно будет заменить его чем-то с помощью os.write(1, binarystring) или прямым вызовом на оболочку вокруг WriteConsoleW. Кажется, что в С++ вам нужно будет сделать то же самое.

Ответ 7

Во-первых, извините, я, вероятно, не имею необходимых шрифтов, поэтому я еще не могу их протестировать.

Что-то выглядит немного подозрительно здесь

// the following is said to be working
SetConsoleOutputCP(CP_UTF8); // output is in UTF8
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize]; 
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m); // <-- upper case %S in wprintf() is used for MultiByte/utf-8
                   //     lower case %s in wprintf() is used for WideChar
printf("%s", m); // <-- does this work as well? try it to verify my assumption

а

// the following is said to have problem
SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,
                     new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl; // <-- you are passing wide char.
// have you tried passing the multibyte equivalent by converting to utf8 first?
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize]; 
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
cout << m << endl;

как насчет

// without setting locale to UTF8, you pass WideChars
wcout << L"¡Hola!" << endl;
// set locale to UTF8 and use cout
SetConsoleOutputCP(CP_UTF8);
cout << utf8_encoded_by_converting_using_WideCharToMultiByte << endl;

Ответ 8

Есть несколько проблем с потоками mswcrt и io.

  1. Trick _setmode (_fileno (stdout), _O_U16TEXT); работает только для MS VC++, а не для MinGW-GCC. Кроме того, иногда это приводит к сбоям в зависимости от конфигурации Windows.
  2. SetConsoleCP (65001) для UTF-8. Может произойти сбой во многих сценариях многобайтовых символов, но всегда есть ОК для UTF-16LE
  3. Вам необходимо восстановить предварительную кодовую страницу консоли при выходе из приложения.

Консоль Windows поддерживает UNICODE с функциями ReadConsole и WriteConsole в режиме UTF-16LE. Фоновый эффект - трубопровод в этом случае не будет работать. Т.е. myapp.exe >> ret.log выводит 0-байтовый файл ret.log. Если вы в порядке с этим фактом, вы можете попробовать мою библиотеку, как показано ниже.

const char* umessage = "Hello!\nПривет!\nПривіт!\nΧαιρετίσματα!\nHelló!\nHallå!\n";

...
#include <console.hpp>
#include <ios>
...

std::ostream& cout = io::console::out_stream();
cout << umessage
<< 1234567890ull << '\n'
<< 123456.78e+09 << '\n'
<< 12356.789e+10L << '\n'
<< std::hex << 0xCAFEBABE
<< std::endl;

Библиотека автоматически преобразует ваш UTF-8 в UTF-16LE и записывает его в консоль с помощью WriteConsole. Также есть ошибки и входные потоки. Еще одно преимущество библиотеки - цвета.

Ссылка на пример приложения: https://github.com/incoder1/IO/tree/master/examples/iostreams

Домашняя страница библиотеки: https://github.com/incoder1/IO

Скриншот:

Ответ 9

У меня была аналогичная проблема, Вывод Юникода на консоль Использование С++ в Windows содержит камень, который вам нужно сделать chcp 65001 в консоли перед запуском вашей программы.

Может быть какой-то способ сделать это программно, но я не знаю, что это такое.

Ответ 10

Правильное отображение западноевропейских символов в консоли Windows

Короче:

  1. используйте chcp чтобы найти, какая кодовая страница работает для вас. В моем случае это было chcp 28591 для Западной Европы.
  2. необязательно сделать это по умолчанию: REG ADD HKCU\Console/v CodePage/t REG_DWORD/d 28591

История открытия

У меня была аналогичная проблема с Java. Он просто косметический, поскольку он включает в себя строки журнала, отправленные на консоль; но это все еще раздражает.

Вывод из нашего приложения Java должен находиться в UTF-8, и он отображается правильно в консоли eclipse. Но в консоли окна, он просто показывает окно рисования символов ASCII: Inicializaci├│n и art├¡culos вместо Inicialización и artículos.

Я наткнулся на связанный с ним вопрос и смешал некоторые ответы, чтобы найти решение, которое сработало для меня. Решение изменяет кодовую страницу, используемую консолью, и использует шрифт, который поддерживает UNICODE (например, consolas или lucida console). Шрифт, который вы можете выбрать в системном меню Windows:

  1. Запустите консоль любым из
    • Win + R затем введите cmd и нажмите клавишу Return.
    • Нажмите клавишу Win и введите cmd а затем ключ return.
  2. Откройте системное меню любым из
    • щелкните значок верхнего левого угла
    • Нажмите комбинацию клавиш Alt + Space
  3. затем выберите "По умолчанию", чтобы изменить поведение всех последующих окон консоли.
  4. перейдите на вкладку "Шрифт"
  5. Выберите Consolas или Lucida console
  6. Нажмите OK

Что касается кодовой страницы, для chcp случая вы можете сделать это с помощью команды chcp а затем вам нужно выяснить, какая кодовая страница верна для вашего набора символов. Несколько ответов предполагали кодировку UTF-8, которая составляет 65001, но эта кодовая страница не работала для моих испанских персонажей.

Другой ответ предложил пакетный скрипт для интерактивного выбора нужной вам кодовой страницы из списка. Там я нашел кодовую страницу для ISO-8859-1, в которой я нуждался: 28591. Таким образом, вы могли бы выполнить

chcp 28591

перед каждым исполнением вашего приложения. Вы можете проверить, какая кодовая страница подходит для вас на странице MSDN идентификаторов кодовых страниц.

Еще один ответ показал, как сохранить выбранную кодовую страницу как стандартную для вашей консоли Windows. Это связано с изменением реестра, поэтому подумайте о том, что вы можете скомпоновать свою машину, используя это решение.

REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591

Это создает CodePage значение с 28591 данных внутри ключа реестра HKCU\Console. И это сработало для меня.

Обратите внимание, что HKCU ("HKEY_CURRENT_USER") предназначен только для текущего пользователя. Если вы хотите изменить его для всех пользователей на этом компьютере, вам нужно будет использовать утилиту regedit и найти/создать соответствующий ключ Console (возможно, вам придется создать ключ Console внутри HKEY_USERS\.DEFAULT)