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

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

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

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

Однако я не нашел способа правильно вывести Unicode с помощью 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; 
} 

person Andrew    schedule 22.03.2010    source источник
comment
Не могли бы вы прояснить, в чем именно он заключается? Вы получаете искаженные / неправильные символы или что-то в этом роде? Вы пытались захватить STDOUT и проверить, что правильные байты отправляются, но не отображаются?   -  person Goyuix    schedule 31.03.2010
comment
Вместо символов отображаются заполнители. Я не особо в это разбирался. Единственное, что я могу сказать, это то, что по какой-то причине одна и та же строка, отправленная в wcout или cout, сходит с ума, в то время как wprintf отображает ее без проблем.   -  person Andrew    schedule 31.03.2010
comment
Только некоторые символы Unicode могут правильно отображаться в консоли Win32. Консоль не поддерживает слишком сложные символы или символы с комбинированными метками, влияющими на их размер. Попробуйте это с помощью WriteConsoleW - если это не так ' Если с этим работать, то это невозможно.   -  person user541686    schedule 29.01.2012


Ответы (15)


Я проверил решение здесь с помощью 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;
}

Снимок экрана:

Юникод в консоли

person DuckMaestro    schedule 29.01.2012
comment
+1 и удалил свой ответ. Это метод, который мы выбрали для Instalog . - person Billy ONeal; 24.04.2012
comment
по-прежнему он не отображает японские символы в моей консоли. - person sarat; 11.04.2013
comment
И еще: перед постоянной строкой стоит неясный L. Как применить это к непостоянным строкам? - person Tomáš Zato - Reinstate Monica; 19.06.2014
comment
+1 для исправления, которое-работает, но следует отметить, что это решение, специфичное для VIsual C ++: оно не обязательно будет работать с g ++. - person Cheers and hth. - Alf; 19.06.2014
comment
Doesn't work when you also have std::cout's От cplusplus.com: Программа не должна смешивать операции вывода на wcout с операциями вывода на cout (или с другими узкоориентированными операциями вывода на stdout): после того, как операция вывода была выполнена на любом из них, стандартный поток вывода приобретает ориентацию (узкий или широкий), который можно безопасно изменить, только вызвав freopen на stdout. - person Roger Dahl; 26.08.2014
comment
@RogerDahl: Я не очень старался, но кажется (в документе MS упоминается в предупреждении), что его можно было вернуть обратно после вызова fflush (). Итак, после явного _setmode(), а затем wcout << ... я выполнил `wcout ‹---------------- flush; fflush (стандартный вывод); _setmode (_fileno (stdout), _O_TEXT); `и вроде работает. - person pepr; 13.01.2016
comment
Похоже, это не работает, если первый вывод представляет собой символ Юникода, а стандартный вывод записывается в файл, например. в вашем примере: std::wcout << L"λληνικά -- Español." << std::endl; - person Luke; 04.11.2016
comment
При работе со строкой в ​​ответе он не работает с более длинными символами, такими как L"안녕하세요." или L"你好!". Похоже, шрифт консоли по умолчанию не поддерживает их (это, вероятно, зависит от настроек локали в Windows). - person Roi Danton; 15.01.2019
comment
Это должен быть принятый ответ. Работает в VS 2019 с C ++ 17 - person Maris B.; 25.07.2019
comment
Связано: Смешивание cout и wcout в одной программе - person Marc.2377; 11.09.2019
comment
Что касается комментария @ Cheersandhth.-Alf, см. здесь. - person Marc.2377; 11.09.2019

Unicode Hello World на китайском языке

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

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

Контур

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

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

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

В свойствах вашего проекта найдите свойства конфигурации -> Общие -> Значения по умолчанию для проекта -> Набор символов. Это должно быть «Использовать набор символов Юникода», а не «Многобайтовый». Это определит для вас макросы препроцессора _UNICODE и UNICODE.

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

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

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

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

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

SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

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

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 для широких символов! И если вы вводите буквальные символы Юникода, подобные этому, в исходный файл, исходный файл должен быть закодирован в Юникоде. Как и в 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;
}

Ваше здоровье !

person David    schedule 25.03.2018
comment
У меня это не работает. Использование C с wprintf (L 你好); - person zezba9000; 31.03.2019
comment
Изменен std :: copy на memcpy (fontInfo.FaceName, myFont, (sizeof (myFont))); и он отлично работает на C ++ с файлом .cpp, но не в том случае, если я компилирую для C с файлом .c. - person zezba9000; 31.03.2019
comment
Nvr mind, его работа. Просто убедитесь, что ваш исходный файл имеет правильную кодировку UTF-8 (с подписью). - person zezba9000; 31.03.2019

Wcout должен иметь языковой стандарт, отличный от CRT. Вот как это можно исправить:

int _tmain(int argc, _TCHAR* argv[])
{
    char* locale = setlocale(LC_ALL, "English"); // Get the CRT's 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;
}

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

person Puppy    schedule 01.04.2010
comment
Спасибо за новую идею, и она сработала для этой строки, но не удалась для чего-то более сложного, например ¡Hola! αβγ амбул привет :) - person Andrew; 01.04.2010
comment
Эта строка не работала и с wprintf для меня, просто оказалась пустой. wcout правильно понял по крайней мере некоторые символы. Не могли бы вы дважды проверить, правильно ли wprintf понимает эту строку? - person Puppy; 01.04.2010
comment
да, если вы выберете правильные шрифты для консоли и запустите ее с помощью cmd.exe, она работает - person Andrew; 01.04.2010
comment
-1 за идею локали + использование _tmain и _TCHAR. исправление языкового стандарта поддерживает только символы в кодировке Windows ANSI для этого языкового стандарта. он не поддерживает общий вывод Unicode (даже UCS2). - person Cheers and hth. - Alf; 19.06.2014
comment
Кажется, работает. К сожалению, теперь у моих чисел есть разделители группировки (тысяч). :( - person Adrian; 16.11.2018

Вы можете использовать библиотеку с открытым исходным кодом {fmt} для переносимой печати текста Unicode, в том числе в Windows, для пример:

#include <fmt/core.h>

int main() {
  fmt::print("èéøÞǽлљΣæča");
}

Вывод:

èéøÞǽлљΣæča

Для этого требуется компиляция с параметром компилятора /utf-8 в MSVC.

Я не рекомендую использовать wcout, потому что он не переносится и даже не работает в Windows без дополнительных усилий, например:

std::wcout << L"èéøÞǽлљΣæča";

напечатает:

├и├й├╕├Ю╟╜╨╗╤Щ╬г├ж─Нa

в русской Windows (ACP 1251, консоль CP 866).

Заявление об ограничении ответственности: я являюсь автором {fmt}.

person vitaut    schedule 28.12.2020

Если вы искали переносимое решение, которое, к сожалению, все еще не входит в стандарт C ++ 20, я могу порекомендовать библиотеку nowide. Он поставляется либо автономно, либо как часть повышения. Вы найдете там множество стандартных аналогов, потребляющих или испускающих char в кодировке utf-8. Да, chars, а не char8_ts (пока). Не стесняйтесь использовать утилиты char8_t-correification, чтобы интерпретировать char8_ts как chars, если ваша программа уже использует их. .

Запрошенный фрагмент кода будет выглядеть так:

#include <boost/nowide/iostream.hpp>
#include <char8_t-remediation.h>

int main()
{
    using boost::nowide::cout;

    cout << U8("¡Hola!") << std::endl;
}

Примечание. Помните о проблеме ориентации потоков. Краткая рекомендация в контексте моего ответа: Используйте исключительно потоки nowide для ввода / вывода и данных в кодировке utf-8.

person neonxc    schedule 24.03.2021

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

person call me Steve    schedule 31.03.2010

Недавно я хотел передать Unicode с Python на консоль Windows, и вот минимум, который мне нужно было сделать:

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

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

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

person newtover    schedule 01.04.2010
comment
Вам действительно нужно установить шрифт, эта часть правильная, и плохой дизайн Windows не использовать по умолчанию шрифт, который работает для приличного диапазона символов Unicode. Однако следующая часть вашего ответа неверна. Вам НЕ нужно устанавливать кодовую страницу на UTF-8/65001 И вызывать WriteConsoleW. Вам нужно сделать одно ИЛИ другое. Установите кодовую страницу, если вы будете вызывать WriteConsoleA и передавать 8-битные строки, включая UTF-8, НО простой вызов WriteConsoleW полностью обходит кодовые страницы и требует UTF-16 (широкие символы). Однако, по моему опыту, установка консоли на 65001 довольно глючна. - person hippietrail; 17.02.2011
comment
@hippietrail: Я не уверен, что буду писать с помощью WriteConsoleW без изменения кодовой страницы на 65001, но, к сожалению, установки только на 65001 недостаточно. По крайней мере, для вывода Unicode из скриптов Python. - person newtover; 24.03.2011

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

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

// 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;
person Afriza N. Arief    schedule 05.04.2010
comment
Это самое интересное. Я попробовал и был удивлен, что это не работает, но все равно спасибо - person Andrew; 05.04.2010

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

  1. Уловка _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 возвращает файл ret.log к 0 байтам. Если вас устраивает этот факт, вы можете попробовать мою библиотеку, как показано ниже.

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

Снимок экрана:

person Victor Gubin    schedule 01.03.2018

Кодировка по умолчанию:

  • Окна UTF-16.
  • Linux UTF-8.
  • MacOS UTF-8.

Мое решение «Шаги» включает нулевые символы \ 0 (избегайте усечения). Без использования функций в заголовке windows.h:

  1. Добавьте макросы для обнаружения Platform.
#if defined (_WIN32) 
#define WINDOWSLIB 1

#elif defined (__ANDROID__) || defined(ANDROID)//Android
#define ANDROIDLIB 1

#elif defined (__APPLE__)//iOS, Mac OS
#define MACOSLIB 1

#elif defined (__LINUX__) || defined(__gnu_linux__) || defined(__linux__)//_Ubuntu - Fedora - Centos - RedHat
#define LINUXLIB 1
#endif
  1. Создайте функции преобразования std :: w строки в std :: string или наоборот.
#include <locale>
#include <iostream>
#include <string>
#ifdef WINDOWSLIB
#include <Windows.h>
#endif

using namespace std::literals::string_literals;

// Convert std::wstring to std::string
std::string WidestringToString(const std::wstring& wstr, const std::string& locale)
{
    if (wstr.empty())
    {
        return std::string();
    }
    size_t pos;
    size_t begin = 0;
    std::string ret;
    size_t  size;
#ifdef WINDOWSLIB
    _locale_t lc = _create_locale(LC_ALL, locale.c_str());
    pos = wstr.find(static_cast<wchar_t>(0), begin);
    while (pos != std::wstring::npos && begin < wstr.length())
    {
        std::wstring segment = std::wstring(&wstr[begin], pos - begin);
        _wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
        std::string converted = std::string(size, 0);
        _wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
        ret.append(converted);
        begin = pos + 1;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
    }
    if (begin <= wstr.length()) {
        std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
        _wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
        std::string converted = std::string(size, 0);
        _wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
    }
    _free_locale(lc);
#elif defined LINUXLIB
    std::string currentLocale = setlocale(LC_ALL, nullptr);
    setlocale(LC_ALL, locale.c_str());
    pos = wstr.find(static_cast<wchar_t>(0), begin);
    while (pos != std::wstring::npos && begin < wstr.length())
    {
        std::wstring segment = std::wstring(&wstr[begin], pos - begin);
        size = wcstombs(nullptr, segment.c_str(), 0);
        std::string converted = std::string(size, 0);
        wcstombs(&converted[0], segment.c_str(), converted.size());
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
    }
    if (begin <= wstr.length()) {
        std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
        size = wcstombs(nullptr, segment.c_str(), 0);
        std::string converted = std::string(size, 0);
        wcstombs(&converted[0], segment.c_str(), converted.size());
        ret.append(converted);
    }
    setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif

    return ret;
}

// Convert std::string to std::wstring
std::wstring StringToWideString(const std::string& str, const std::string& locale)
{
    if (str.empty())
    {
        return std::wstring();
    }

    size_t pos;
    size_t begin = 0;
    std::wstring ret;
    size_t  size;

#ifdef WINDOWSLIB
    _locale_t lc = _create_locale(LC_ALL, locale.c_str());
    pos = str.find(static_cast<char>(0), begin);
    while (pos != std::string::npos) {
        std::string segment = std::string(&str[begin], pos - begin);
        std::wstring converted = std::wstring(segment.size() + 1, 0);
        _mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = str.find(static_cast<char>(0), begin);
    }
    if (begin < str.length()) {
        std::string segment = std::string(&str[begin], str.length() - begin);
        std::wstring converted = std::wstring(segment.size() + 1, 0);
        _mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
        converted.resize(size - 1);
        ret.append(converted);
    }
    _free_locale(lc);
#elif defined LINUXLIB
    std::string currentLocale = setlocale(LC_ALL, nullptr);
    setlocale(LC_ALL, locale.c_str());
    pos = str.find(static_cast<char>(0), begin);
    while (pos != std::string::npos) {
        std::string segment = std::string(&str[begin], pos - begin);
        std::wstring converted = std::wstring(segment.size(), 0);
        size = mbstowcs(&converted[0], &segment[0], converted.size());
        converted.resize(size);
        ret.append(converted);
        ret.append({ 0 });
        begin = pos + 1;
        pos = str.find(static_cast<char>(0), begin);
    }
    if (begin < str.length()) {
        std::string segment = std::string(&str[begin], str.length() - begin);
        std::wstring converted = std::wstring(segment.size(), 0);
        size = mbstowcs(&converted[0], &segment[0], converted.size());
        converted.resize(size);
        ret.append(converted);
    }
    setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif

    return ret;
}
  1. Выведите std :: string. Отметьте суффикс RawString.

Код Linux. Прямая печать std :: string с помощью std :: cout.
Если у вас есть std :: wstring.
1. Преобразование в std :: string.
2. Печать с std :: cout.

std::wstring x = L"\0\001日本ABC\0DE\0F\0G????\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
std::cout << "RESULT=" << result << std::endl;
std::cout << "RESULT_SIZE=" << result.size() << std::endl;

В Windows, если вам нужно распечатать Unicode. Нам нужно использовать WriteConsole для печати символов Unicode из std :: wstring или std :: string.

void WriteUnicodeLine(const std::string& s)
{
#ifdef WINDOWSLIB
    WriteUnicode(s);
    std::cout << std::endl;
#elif defined LINUXLIB
    std::cout << s << std::endl;
#elif defined MACOSLIB
#endif
}

void WriteUnicode(const std::string& s)
{

#ifdef WINDOWSLIB
    std::wstring unicode = Insane::String::Strings::StringToWideString(s);
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), static_cast<DWORD>(unicode.length()), nullptr, nullptr);
#elif defined LINUXLIB
    std::cout << s;
#elif defined MACOSLIB
#endif


}

void WriteUnicodeLineW(const std::wstring& ws)
{

#ifdef WINDOWSLIB
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
    std::cout << std::endl;
#elif defined LINUXLIB
    std::cout << String::Strings::WidestringToString(ws)<<std::endl;
#elif defined MACOSLIB
#endif


}

void WriteUnicodeW(const std::wstring& ws)
{

#ifdef WINDOWSLIB
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
#elif defined LINUXLIB
    std::cout << String::Strings::WidestringToString(ws);
#elif defined MACOSLIB
#endif

}

Код Windows. Использование функции WriteLineUnicode или WriteUnicode. Тот же код можно использовать для Linux.

std::wstring x = L"\0\001日本ABC\0DE\0F\0G????\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
WriteLineUnicode(u8"RESULT" + result);
WriteLineUnicode(u8"RESULT_SIZE" + std::to_string(result.size()));

Наконец-то в Windows. Вам нужна мощная и полная поддержка символов Unicode в консоли. Я рекомендую ConEmu и установить как терминал по умолчанию в Windows.

Тестируйте на Microsoft Visual Studio и Jetbrains Clion.

  • Протестировано в Microsoft Visual Studio 2017 с VC ++; std = c ++ 17. (Проект Windows)
  • Протестировано в Microsoft Visual Studio 2017 с g ++; std = c ++ 17. (Проект Linux)
  • Протестировано на Jetbrains Clion 2018.3 с g ++; std = c ++ 17. (Linux Toolchain / Remote)

QA

В. Почему вы не используете <codecvt> функции и классы заголовков?
А. Не рекомендуется Удаленные или устаревшие функции невозможно построить на VC ++, но нет проблем на g ++. Я предпочитаю 0 предупреждений и головной боли.

Q. wstring в Windows перемежается.
A. Устарело Удаленные или устаревшие функции невозможно построить на VC ++, но нет проблем на g ++. Я предпочитаю 0 предупреждений и головной боли.

Q. std :: wstring является кроссплатформенным?
A. Нет. std :: wstring использует элементы wchar_t. В Windows размер wchar_t составляет 2 байта, каждый символ хранится в единицах UTF-16, если символ больше, чем U + FFFF, символ представляется в двух единицах UTF-16 (2 элемента wchar_t), называемых суррогатными парами. В Linux размер wchar_t составляет 4 байта, каждый символ хранится в одном элементе wchar_t, никаких суррогатных пар не требуется. Отметьте Standard типы данных в UNIX, Linux и Windows.

Q. std :: string является кроссплатформенным?
A. Да. std :: string использует элементы char. Тип char гарантированно имеет одинаковый размер байта во всех компиляторах. Размер типа char составляет 1 байт. Отметьте Standard типы данных в UNIX, Linux и Windows.

person Joma    schedule 22.02.2019

запуск консольного приложения из VS2017 под Win10 с региональными настройками Великобритании потребовал от меня:

  1. набор инструментов VS2017 ›Среда› Шрифты и цвета ›Шрифт: например,« Lucida »
  2. сохранять исходные файлы C ++ с кодировкой Unicode (UTF-8 с подписью) - Кодовая страница 650001, чтобы вы могли вводить буквы L с диакритическими знаками Lâéïôù без предупреждений компилятора, но избегайте повсюду двухбайтовых символов
  3. скомпилировать с помощью свойств конфигурации ›Общие› Набор символов ›Использовать многобайтовый .. и Свойства конфигурации› C / C ++ ›Все параметры› Дополнительные параметры ›/ utf-8 flag
  4. #include ‹iostream›, ‹io.h› и ‹fcntl.h›
  5. выполнить непонятный '_setmode (_fileno (stdout), _O_WTEXT);' один раз при запуске приложения
  6. забудьте 'cout ‹< ...;' и используйте только 'wcout ‹< ...;'

Для заметок VS2015 на Win7 требовался SetConsoleOutputCP (65001); и позволял микшировать выходы через wcout и cout.

person berhauz    schedule 17.03.2021

В моем случае я читаю файл UTF-8 и печатаю в Console, я считаю, что wifstream работает очень хорошо, даже в отладчике Visual Studio правильно отображаются слова UTF-8 (я читаю традиционный китайский), из этот пост:

#include <sstream>
#include <fstream>
#include <codecvt>

std::wstring readFile(const char* filename)
{
    std::wifstream wif(filename);
    wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
    std::wstringstream wss;
    wss << wif.rdbuf();
    return wss.str();
}

//  usage
std::wstring wstr2;
wstr2 = readFile("C:\\yourUtf8File.txt");
wcout << wstr2;
person yu yang Jian    schedule 15.04.2021

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

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

person mr calendar    schedule 07.02.2011

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

Короче говоря:

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

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

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

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

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

  1. Start a console by any one of
    • Win + R then type cmd and hit the Return key.
    • Нажмите клавишу Win и введите cmd, а затем клавишу return.
  2. Open the system menu by any one of
    • click the upper left corner icon
    • Нажмите комбинацию клавиш Alt + Space
  3. затем выберите «По умолчанию», чтобы изменить поведение всех последующих окон консоли.
  4. перейдите на вкладку "Шрифт"
  5. Выберите Consolas или Lucida console
  6. Нажмите OK

Что касается кодовой страницы, в разовом случае вы можете сделать это с помощью команды 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)

person manuelvigarcia    schedule 20.09.2017

person    schedule
comment
вы можете использовать символьные константы, такие как \x45, чтобы строка работала независимо от исходной кодировки - person phuclv; 06.06.2014
comment
-1 нехороший совет обмануть компилятор, приводящий к некорректной компиляции широких литералов. - person Cheers and hth. - Alf; 19.06.2014
comment
Вы действительно хотите использовать escape-символы, чтобы не зависеть от того, как символы, отличные от ASCII, в исходном коде сохраняются редактором и интерпретируются компилятором. Например, строку UTF-8 из ответа можно переносимо записать как "tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2". - person user4815162342; 16.11.2014
comment
SetConsoleCP влияет только на ввод, поэтому неудивительно, что это не работает в вашем примере. Это SetConsoleOutputCP, который контролирует кодировку вывода. - person rdb; 22.11.2017