Извеждане на 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);

Въпреки това не намерих никакъв начин да извеждам уникод правилно с 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;
}

Екранна снимка:

Unicode в конзолата

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; ffflush(stdout); _setmode(_fileno(stdout), _O_TEXT);` и изглежда работи. - person pepr; 13.01.2016
comment
Това изглежда не работи, ако първият изход е уникод-символ и stdout е записан във файл, напр. във вашия пример: 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 на китайски

Това е 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. Създадох празно конзолно приложение. Настройките по подразбиране са наред. Но ако имате проблеми или използвате друга идея, може да искате да проверите тези:

В свойствата на вашия проект намерете конфигурационни свойства -> Общи -> Настройки по подразбиране на проекта -> Набор от символи. Трябва да е „Използване на Unicode символен набор“, а не „Многобайтов“. Това ще дефинира _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 Там е макрос на препроцесор за тази кодова страница: CP_UTF8. Трябваше да задам и двете, входната и изходната кодова страница. Когато пропуснах някой от тях, изходът беше неправилен.

SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

Може също да искате да проверите булевите връщани стойности на тези функции.

3. Изберете шрифт

Досега не намерих конзолен шрифт, който поддържа всеки знак. Така че трябваше да избера един. Ако искате да изведете знаци, които са отчасти налични само в един шрифт и отчасти в друг шрифт, тогава вярвам, че е невъзможно да се намери решение. Може би само ако има шрифт, който поддържа всеки знак. Но също така не разгледах как да инсталирам шрифт.

Мисля, че не е възможно да се използват два различни шрифта в един и същ прозорец на конзолата едновременно.

Как да намеря съвместим шрифт? Отворете вашата конзола, отидете на свойствата на прозореца на конзолата, като щракнете върху иконата в горния ляв ъгъл на прозореца. Отидете в раздела шрифтове и изберете шрифт и щракнете върху OK. След това опитайте да въведете героите си в прозореца на конзолата. Повторете това, докато намерите шрифт, с който можете да работите. След това запишете името на шрифта.

Също така можете да промените размера на шрифта в прозореца със свойства. Ако сте намерили размер, от който сте доволни, отбележете стойностите на размера, които се показват в прозореца със свойства в секцията „избран шрифт“. Ще покаже ширина и височина в пиксели.

За да зададете програмно шрифта, който използвате:

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 ум, работи. Просто трябва да се уверите, че вашият изходен файл е с правилното 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! αβγ ambulō привет :) - 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. Предлага се или самостоятелно, или като част от boost. Там ще намерите много стандартни двойници, които консумират или излъчват utf-8 кодирани chars. Да, chars, не char8_ts (все още). Чувствайте се свободни да използвате char8_t-remediation помощни програми, за да интерпретирате 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 и ето минимума, който трябваше да направя:

  • Трябва да зададете шрифт на конзолата на този, който покрива уникод символите. Няма голям избор: Свойства на конзолата > Шрифт > Конзола 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 довежда до 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

Екранна снимка:

person Victor Gubin    schedule 01.03.2018

Стандартно кодиране на:

  • Windows UTF-16.
  • Linux UTF-8.
  • MacOS UTF-8.

Стъпките на моето решение включват нулеви знаци \0 (избягвайте съкращаване). Без използване на функции в заглавката на windows.h:

  1. Добавете макроси за откриване на платформа.
#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::wstring в 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 за печат на уникод знаци от 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. Имате нужда от мощна и пълна поддръжка за уникод знаци в конзолата. Препоръчвам 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 / Дистанционно)

QA

В. Защо не използвате <codecvt> заглавни функции и класове?.
А. Отхвърляне на Премахнати или отхвърлени функции невъзможно изграждане на VC++, но няма проблеми на g++. Предпочитам 0 предупреждения и главоболия.

Q. wstring в Windows са взаимозаменяеми.
A. Отхвърляне на Премахнати или остарели функции невъзможно изграждане на VC++, но няма проблеми на g++. Предпочитам 0 предупреждения и главоболия.

В. std ::wstring е междуплатформен?
А. Не. std::wstring използва wchar_t елементи. В Windows размерът wchar_t е 2 байта, всеки символ се съхранява в UTF-16 единици, ако знакът е по-голям от U+FFFF, символът се представя в две UTF-16 единици (2 wchar_t елемента), наречени сурогатни двойки. В Linux wchar_t размерът е 4 байта, всеки символ се съхранява в един wchar_t елемент, не са необходими сурогатни двойки. Проверете Стандартен типове данни в UNIX, Linux и Windows.

В. std ::string е междуплатформен?
А. Да. std::string използва char елементи. типът char е гарантиран, че е с еднакъв размер на байта във всички компилатори. размерът на типа char е 1 байт. Проверете Стандартен типове данни в UNIX, Linux и Windows.

person Joma    schedule 22.02.2019

стартирането на конзолно приложение от VS2017 под Win10 с регионални настройки за Обединеното кралство изискваше от мен:

  1. задайте VS2017 инструменти › Околна среда › Шрифтове и цветове › Шрифт: „Lucida“ например
  2. запазете изходни файлове на C++ с кодиране Unicode (UTF-8 със сигнатура) - кодова страница 650001, така че да можете да въвеждате символи с ударение Lâéïôù без предупреждения на компилатора, но избягвайте двубайтови знаци навсякъде
  3. компилирайте с конфигурационни свойства › Общи › CharacterSet › Използвайте Multi-byte.. и конфигурационни свойства › C/C++ › Всички опции › Допълнителни опции › /utf-8 флаг
  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 debugger показва 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

Имах подобен проблем, Output Unicode to console Using C++, in 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
Наистина искате да използвате екрани, за да не зависи от това как не-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