Output Unicode strings in Windows console
Asked Answered
I

17

86

Hi I was trying to output unicode string to a console with iostreams and failed.

I found this: Using unicode font in c++ console app and this snippet works.

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

However, I did not find any way to output unicode correctly with iostreams. Any suggestions?

This does not work:

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

EDIT I could not find any other solution than to wrap this snippet around in a stream. Hope, somebody has better ideas.

//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; 
} 
Indigested answered 22/3, 2010 at 12:15 Comment(3)
Could you clarify exactly how it is failing? Are you getting garbled/wrong characters or something? Have you tried capturing STDOUT and verify the correct bytes are being sent but not displayed maybe?Bats
It shows placeholders instead of characters. I did not look very much deep into it. The only thing I can tell, is that for some reason, the same string sent to wcout or cout goes crazy while wprintf displays it with no problem.Indigested
Only some Unicode characters can be properly displayed inside the Win32 console. The console doesn't support characters that are too complicated or ones that have combining marks that affect their size. Try it with WriteConsoleW -- if it doesn't work with that, then it's impossible.Buster
D
109

I have verified a solution here using Visual Studio 2010. Via this MSDN article and MSDN blog post. The trick is an obscure call to _setmode(..., _O_U16TEXT).

Solution:

#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;
}

Screenshot:

Unicode in console

Ditter answered 29/1, 2012 at 7:4 Comment(12)
+1 and deleted my answer. This is the method we chose for Instalog.Idiomatic
still it doesn't display Japanese characters in my console.Izabel
And more: theres an obscure L before the constant string. How to apply that on non-constant strings?Bream
+1 for the fix-that-works, but one should note that that's a VIsual C++ specific solution: it won't necessarily work with g++.Requirement
Doesn't work when you also have std::cout's From cplusplus.com: A program should not mix output operations on wcout with output operations on cout (or with other narrow-oriented output operations on stdout): Once an output operation has been performed on either, the standard output stream acquires an orientation (either narrow or wide) that can only be safely changed by calling freopen on stdout.Politicking
@RogerDahl: I did not try very hard, but it seems (the MS doc is mentioning in the Caution) that it could be set back after calling fflush(). So, after explicit _setmode(), then wcout << ..., I did ` wcout << flush; fflush(stdout); _setmode(_fileno(stdout), _O_TEXT);` and it seems to work.Aldous
This doesn't seem to work if the first output is a unicode-character and stdout is written to a file, e.g. in your example: std::wcout << L"λληνικά -- Español." << std::endl;Endoderm
While working for the string in the answer, it doesn't work for longer characters like L"안녕하세요." or L"你好!". It seems, the default console font doesn't support those (this likely depends on the locale settings in Windows).Bernoulli
This should be accepted answer. Works in VS 2019 with C++17Amador
Related: Mixing cout and wcout in same programGeyer
Regarding the comment by @Cheersandhth.-Alf, see here.Geyer
If I try to print שלום it will br printed reversedDiaphanous
J
24

In C++23 you'll be able to use std::print to portably print Unicode text:

import std;

int main() {
  std::print("Шчучыншчына");
}

Output:

Шчучыншчына

This requires compiling with the /utf-8 compiler option in MSVC.

Until it is available you can use the open-source {fmt} library, std::print is based on. For example:

#include <fmt/core.h>

int main() {
  fmt::print("Шчучыншчына");
}

I don't recommend using wcout because it is non-portable and doesn't even work on Windows without extra efforts, for example:

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

will print:

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

in Cyrillic Windows (ACP 1251, console CP 866).

Disclaimer: I'm the author of {fmt} and C++23 std::print.

Jemimah answered 28/12, 2020 at 15:8 Comment(4)
But how does it work? How does print adapts its behavior to windows terminal code page?Mika
It uses Unicode API and avoids code pages completely.Jemimah
Do you have a link to Unicode API? Is it a windows API? Is it the WIN32 wide string variant? (wcout)Mika
github.com/fmtlib/fmt/blob/…Jemimah
C
13

Unicode Hello World in Chinese

Here is a Hello World in Chinese. Actually it is just "Hello". I tested this on Windows 10, but I think it might work since Windows Vista. Before Windows Vista it will be hard, if you want a programmatic solution, instead of configuring the console / registry etc. Maybe have a look here if you really need to do this on Windows 7: Change console Font Windows 7

I dont want to claim this is the only solution, but this is what worked for me.

Outline

  1. Unicode project setup
  2. Set the console codepage to unicode
  3. Find and use a font that supports the characters you want to display
  4. Use the locale of the language you want to display
  5. Use the wide character output i.e. std::wcout

1 Project Setup

I am using Visual Studio 2017 CE. I created a blank console app. The default settings are alright. But if you experience problems or you use a different ide you might want to check these:

In your project properties find configuration properties -> General -> Project Defaults -> Character Set. It should be "Use Unicode Character Set" not "Multi-Byte". This will define _UNICODE and UNICODE preprocessor macros for you.

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

Also I think we should use wmain function instead of main. They both work, but in a unicode environment wmain may be more convenient.

Also my source files are UTF-16-LE encoded, which seems to be the default in Visual Studio 2017.

2 Console Codepage

This is quite obvious. We need the unicode codepage in the console. If you want to check your default codepage, just open a console and type chcp withou any arguments. We have to change it to 65001, which is the UTF-8 codepage. Windows Codepage Identifiers There is a preprocessor macro for that codepage: CP_UTF8. I needed to set both, the input and output codepage. When I omitted either one, the output was incorrect.

SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);

You might also want to check the boolean return values of those functions.

3 Choose a Font

Until yet I didnt find a console font that supports every character. So I had to choose one. If you want to output characters which are partly only available in one font and partly in another font, then I believe it is impossible to find a solution. Only maybe if there is a font out there that supports every character. But also I didnt look into how to install a font.

I think it is not possible to use two different fonts in the same console window at the same time.

How to find a compatible font? Open your console, go to the properties of the console window by clicking on the icon in the upper left of the window. Go to the fonts tab and choose a font and click ok. Then try to enter your characters in the console window. Repeat this until you find a font you can work with. Then note down the name of the font.

Also you can change the size of the font in the properties window. If you found a size you are happy with, note down the size values that are displayed in the properties window in the section "selected font". It will show width and height in pixels.

To actually set the font programmatically you use:

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

See my example at the end of this answer for details. Or look it up in the fine manual: SetCurrentConsoleFont. This function only exists since Windows Vista.

4 Set the locale

You will need to set the locale to the locale of the language which characters you want to print.

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

The return value is interesting. It will contain a string to describe exactly wich locale was chosen. Just give it a try :-) I tested with chinese and german. More info: setlocale

5 Use wide character output

Not much to say here. If you want to output wide characters, use this for example:

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

Oh, and dont forget the L prefix for wide characters! And if you type literal unicode characters like this in the source file, the source file must be unicode encoded. Like the default in Visual Studio is UTF-16-LE. Or maybe use notepad++ and set the encoding to UCS-2 LE BOM.

Example

Finally I put it all together as an example:

#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;
}

Cheers !

Edit on 2021-11-20

Maybe you can also try to use the new Windows Terminal. It seems to print unicode out of the box. You will still need to configure a font that supports your characters in the settings. It is developed by Microsoft as OpenSource on github and you can also install it from the Microsoft Store. I successfully tried this on Windows 10.

Centrifuge answered 25/3, 2018 at 19:2 Comment(4)
This isn't working for me. Using C with "wprintf(L"你好");"Kwakiutl
Changed std::copy to "memcpy(fontInfo.FaceName, myFont, (sizeof(myFont)));" and it works in C++ just fine with a .cpp file but not if I compile for C with a .c file.Kwakiutl
Nvr mind, its working. Just have to make sure your source file is the right UTF-8 encoding (with signature).Kwakiutl
SetCurrentConsoleFontEx function is in support mode and no longer encouraged to be used in new command line apps.Bema
R
3

The wcout must have the locale set differently to the CRT. Here's how it can be fixed:

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

I just tested it, and it displays the string here absolutely fine.

Roentgenograph answered 1/4, 2010 at 0:2 Comment(5)
Thanks for a new idea and it worked for this string but it fails for something more complicated like "¡Hola! αβγ ambulō привет :)"Indigested
That string didn't work on wprintf for me either, just came out as a total blank. wcout got at least some of the characters right. Could you double check that wprintf gets this string right?Roentgenograph
yes, if you select correct fonts for the console and start it with cmd.exe it worksIndigested
-1 for the locale idea + use of _tmain and _TCHAR. fixing the locale only supports characters in that locale's Windows ANSI encoding. it doesn't support general Unicode output (not even UCS2).Requirement
Appears to work. Unfortunately, now my numbers have grouping (thousands) separators. :(Wolfie
D
3

I used a palindromes word in Hebrew, as Console application may display right-to-left strings reversed.

Here is my multiplatform code:

#include <iostream> 
#ifdef _WIN32 // #A
#include <io.h> // #B
#include <fcntl.h> // #C
#else // #D
#include <locale> // #E
#endif

int main() 
{
#ifdef _WIN32 // #A
    _setmode(_fileno(stdout), _O_U16TEXT); // #F
    std::wcout << L"אבא" << std::endl; // #G
#else // #D
    std::locale::global(std::locale("")); // #H
    std::wcout.imbue(std::locale()); // #I
    std::wcout << L"אבא" << std::endl; // #G
#endif
}

#A - Preprocessor directive for Windows-specific code

#B - Include the io.h library for low-level I/O operations

#C - Include the fcntl.h library for file control operations

#D - Preprocessor directive for non-Windows code

#E - Include the locale library for locale-specific operations

#F - Set the mode of stdout to use Unicode

#G - Print the Hebrew word to the console

#H - Set the global locale to the user's preferred locale

#I - Set the locale of wcout to the global locale

Diaphanous answered 13/5, 2023 at 20:28 Comment(0)
M
2

Solution 1: use WCHAR

One thing that always works: use wide char all the places. Like,

const wchar_t* str = L"你好\n";
DWORD nwritten = 0;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, 3, &nwritten, NULL);

Unicode is language neutral. You can use any language and won't have encoding issues. You want use UTF-8? Fine. Use MultiByteToWideChar to convert it wide char string first.

Before continue reading the other solution below, please note this one has a unique advantage: it doesn't depend on the system or the user's locale settings.

Solution 2: Set system locale and user locale properly. And they should be the same.

I assume UTF-8 locale for Windows isn't in the picture yet. Then you need to know which language(Chinese, French?) you would use, and change your system settings to match it. There is system level setting: Change system code page

And a user level setting: enter image description here

Please set both of them to the same language.

Then, in your program, insert "setlocale(LC_ALL, "");" to your main function. It's a universal rule, no matter which OS you use, whenever you want to use the standard library to process charsets other than ASCII, you should have this line of code. Otherwise, the locale defaults to "C" and it only contains ASCII. Then you can start to use std::wcout and C functions like fputws.

Marcelinomarcell answered 2/1, 2022 at 1:40 Comment(0)
J
1

SetConsoleCP() and chcp does not the same!

Take this program snippet:

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

The source code must be saved as UTF-8 without BOM (Byte Order Mark; Signature). Then, the Microsoft compiler cl.exe takes the UTF-8 strings as-is.
If this code is saved with BOM, cl.exe transcodes the string to ANSI (i.e. CP1252), which doesn't match to CP65001 (= UTF-8).

Change the display font to Lucidia Console, otherwise, UTF-8 output will not work at all.

  • Type: chcp
  • Answer: 850
  • Type: test.exe
  • Answer: tr├ñnen├╝berstr├ÂmtÔäó
  • Type: chcp
  • Answer: 65001 - This setting has changed by SetConsoleCP() but with no useful effect.
  • Type: chcp 65001
  • Type: test.exe
  • Answer: tränenüberströmt™ - All OK now.

Tested with: German Windows XP SP3

Janus answered 17/7, 2012 at 12:40 Comment(4)
you can use character constants like \x45 to make the string works regardless of source encodingRedwood
-1 ungood advice to trick the compiler, resulting in incorrect compilation of wide literals.Requirement
You really want to use escapes so as not to depend on how the non-ASCII characters in the source are saved by the editor and interpreted by the compiler. For example, the UTF-8 string from the answer can be portably written as "tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2".Mongoose
SetConsoleCP only affects input, so it is no surprise at all that it doesn't work in your example. It is SetConsoleOutputCP that controls output encoding.Audit
S
1

Default encoding on:

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

My solution Steps, includes null chars \0 (avoid truncated). Without using functions on windows.h header:

  1. Add Macros to detect 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. Create conversion functions std::wstring to std::string or viceversa.
#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. Print std::string. Check RawString Suffix.

Linux Code. Print directly std::string using std::cout.
If you have std::wstring.
1. Convert to std::string.
2. Print with 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;

On Windows if you need to print unicode. We need to use WriteConsole for print unicode chars from std::wstring or 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 Code. Using WriteLineUnicode or WriteUnicode function. Same code can be used for 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()));

Finally on Windows. You need a powerfull and complete support for unicode chars in console. I recommend ConEmu and set as default terminal on Windows.

Test on Microsoft Visual Studio and Jetbrains Clion.

  • Tested on Microsoft Visual Studio 2017 with VC++; std=c++17. (Windows Project)
  • Tested on Microsoft Visual Studio 2017 with g++; std=c++17. (Linux Project)
  • Tested on Jetbrains Clion 2018.3 with g++; std=c++17. (Linux Toolchain / Remote)

QA

Q. Why you not use <codecvt> header functions and classes?.
A. Deprecate Removed or deprecated features impossible build on VC++, but no problems on g++. I prefer 0 warnings and headaches.

Q. wstring on Windows are interchan.
A. Deprecate Removed or deprecated features impossible build on VC++, but no problems on g++. I prefer 0 warnings and headaches.

Q. std ::wstring is cross platform?
A. No. std::wstring uses wchar_t elements. On Windows wchar_t size is 2 bytes, each character is stored in UTF-16 units, if character is bigger than U+FFFF, the character is represented in two UTF-16 units(2 wchar_t elements) called surrogate pairs. On Linux wchar_t size is 4 bytes each character is stored in one wchar_t element, no needed surrogate pairs. Check Standard data types on UNIX, Linux, and Windows.

Q. std ::string is cross platform?
A. Yes. std::string uses char elements. char type is guaranted that is same byte size in all compilers. char type size is 1 byte. Check Standard data types on UNIX, Linux, and Windows.

Simonton answered 22/2, 2019 at 19:26 Comment(0)
A
1

If you were looking for a portable solution, which is unfortunately still not part of a standard as of C++20, I can recommend the nowide library. It comes either standalone or as part of boost. You'll find many standard counterparts consuming or emitting utf-8 encoded chars there. Yes, chars, not char8_ts (yet). Feel free to use char8_t-remediation utilities to interpret char8_ts as chars, if your program sports them already.

The requested code snippet would look like this:

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

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

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

Note: Please be aware of streams orientation issue. A short recommendation in the context of my answer would be: Use exclusively nowide streams for input / output and utf-8 encoded data.

Apostrophe answered 24/3, 2021 at 8:53 Comment(0)
I
1

Testing with VS2019 with UNICODE console app on Win10 found the following testing Spanish and Japanese:

If you just wprintf a string then you get the wrong characters for Spanish (Japanese not tested but sure it won't work). It appears the default "C" locale default is ASCII (the traditional extended ASCII collating table for PC's).

using: setlocale(LC_ALL, ""); sets the correct code page to CP1252 when using Spanish (Mexico) Windows language setting and the output is good (lucida console font). However, Japanese output (using Japanese Windows Language) is suppressed (meaning no output for those characters, normal Latin characters are output).

using: '_setmode(_fileno(stdout), _O_U16TEXT);` output works correctly for all. However all output is 16bit so redirection to a file outputs 16bit characters.

using: printf and UTF-8 text output with SetConsoleOutputCP(CP_UTF8) also works (but not if you set it after setlocale(LC_ALL, ""); - I had to remove that for the output to work).

Fonts: For the Asian characters use MS Mincho, for the others you can use Lucida Console.

Idalla answered 8/3, 2022 at 1:24 Comment(0)
C
0

I don't think there is an easy answer. looking at Console Code Pages and SetConsoleCP Function it seems that you will need to set-up an appropriate codepage for the character-set you're going to output.

Compiler answered 31/3, 2010 at 15:41 Comment(0)
L
0

Recenly I wanted to stream unicode from Python to windows console and here is the minimum I needed to make:

  • You should set console font to the one covering unicode symbols. There is not a wide choise: Console properties > Font > Lucida Console
  • You should change the current console codepage: run chcp 65001 in the Console or use the corresponding method in the C++ code
  • write to console using WriteConsoleW

Look through an interesing article about java unicode on windows console

Besides, in Python you can not write to default sys.stdout in this case, you will need to substitute it with something using os.write(1, binarystring) or direct call to a wrapper around WriteConsoleW. Seems like in C++ you will need to do the same.

Lockman answered 1/4, 2010 at 8:13 Comment(2)
You do need to set the font, this part is correct and it is poor design of Windows not to default to a font that works for a decent range of Unicode characters. However the next part of your answer is wrong. You do NOT need to set the codepage to UTF-8/65001 AND call WriteConsoleW. You need to do one OR the other. Set the codepage if you will be calling WriteConsoleA and passing in 8-bit strings including UTF-8, BUT just calling WriteConsoleW completely bypasses codepages and requires UTF-16 (wide chars). In my experience however, setting the console to 65001 is quite buggy.Ramires
@hippietrail: I am not sure about writing with WriteConsoleW without changing the codepage to 65001, but setting to 65001 only is unfortunately not enough. At least for unicode output from Python scripts.Lockman
S
0

First, sorry I probably don't have the fonts required so I cannot test it yet.

Something looks a bit fishy here

// 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

while

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

what about

// 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;
Schacker answered 5/4, 2010 at 9:53 Comment(1)
That is the fun part. I tried it and I was surprised that it does not work, but thanks anywaysIndigested
N
0

Correctly displaying Western European characters in the windows console

Long story short:

  1. use chcp to find which codepage works for you. In my case it was chcp 28591 for Western Europe.
  2. optionally make it the default: REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591

History of the discovery

I had a similar problem, with Java. It is just cosmetic, since it involves log lines sent to the console; but it is still annoying.

The output from our Java application is supposed to be in UTF-8 and it displays correctly in eclipse's console. But in windows console, it just shows the ASCII box-drawing characters: Inicializaci├│n and art├¡culos instead of Inicialización and artículos.

I stumbled upon a related question and mixed some of the answers to get to the solution that worked for me. The solution is changing the codepage used by the console and using a font that supports UNICODE (like consolas or lucida console). The font you can select in the system menu of the Windows cosole:

  1. Start a console by any one of
    • Win + R then type cmd and hit the Return key.
    • Hit the Win key and type cmd followed by the return key.
  2. Open the system menu by any one of
    • click the upper left corner icon
    • Hit the Alt + Space key combination
  3. then select "Default" to change the behavior of all subsequent console windows
  4. click the "Font" tab
  5. Select Consolas or Lucida console
  6. Click OK

Regarding the codepage, for a one-off case, you can get it done with the command chcp and then you have to investigate which codepage is correct for your set of characters. Several answers suggested UTF-8 codepage, which is 65001, but that codepage didn't work for my Spanish characters.

Another answer suggested a batch script to interactively selecting the codepage you wanted from a list. There I found the codepage for ISO-8859-1 I needed: 28591. So you could execute

chcp 28591

before each execution of your application. You might check which code page is right for you in the Code Page Identifiers MSDN page.

Yet another answer indicated how to persist the selected codepage as the default for your windows console. It involves changing the registry, so consider yourself warned that you might brick your machine by using this solution.

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

This creates the CodePage value with the 28591 data inside the HKCU\Console registry key. And that did work for me.

Please note that HKCU ("HKEY_CURRENT_USER") is only for the current user. If you want to change it for all users in that computer, you'll need to use the regedit utility and find/create the corresponding Console key (probably you'll have to create a Console key inside HKEY_USERS\.DEFAULT)

Natalyanataniel answered 20/9, 2017 at 9:3 Comment(0)
P
0

There are a few issues with the mswcrt and io streams.

  1. Trick _setmode(_fileno(stdout), _O_U16TEXT); working only for MS VC++ not MinGW-GCC. Moreover sometimes it is brings to crashes depending on Windows configuration.
  2. SetConsoleCP(65001) for UTF-8. May fail in many multibyte character scenarios, but is is always OK for UTF-16LE
  3. You need to restore previews console codepage on application exit.

Windows console supports UNICODE with the ReadConsole and WriteConsole functions in UTF-16LE mode. Background effect - piping in this case will not work. I.e. myapp.exe >> ret.log brings to 0 byte ret.log file. If you are ok with this fact you can try my library as following.

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;

Library will auto-convert your UTF-8 into UTF-16LE and write it into console using WriteConsole. As well as there are error and input streams. Another library benefit - colors.

Link on example app: https://github.com/incoder1/IO/tree/master/examples/iostreams

The library homepage: https://github.com/incoder1/IO

Screenshot:

Pallaten answered 1/3, 2018 at 18:24 Comment(0)
B
0

running a console App from VS2017 under Win10 with UK regional settings required me to:

  1. set VS2017 tools > Environment > Fonts and Colors > Font: 'Lucida' for instance
  2. save C++ source files with encoding "Unicode (UTF-8 with signature) - Codepage 650001" so that you can type-in accented character litterals L"âéïôù" without compiler warnings, yet avoid double byte characters everywhere
  3. compile with Configuration Properties > General > CharacterSet > "Use Multi-byte.." and the Configuration Properties > C/C++ > All Options > Additional Options > "/utf-8" flag
  4. #include <iostream>, <io.h>, and <fcntl.h>
  5. execute an obscure '_setmode(_fileno(stdout), _O_WTEXT);' once at the start of the App
  6. forget 'cout <<... ;' and use only 'wcout << ... ;'

For memo, VS2015 on Win7 required a 'SetConsoleOutputCP(65001);' and allowed to mix outputs via wcout and cout.

Billups answered 17/3, 2021 at 14:59 Comment(0)
F
0

In my case I'm reading UTF-8 file and print to Console, I find wifstream works very good, even in visual studio debugger shows UTF-8 words correctly (I'm reading traditional chinese), from this post:

#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;
Fault answered 15/4, 2021 at 15:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.