簡體   English   中英

Output Unicode 控制台中的字符串 Windows

[英]Output Unicode strings in Windows console

您好,我試圖將 output unicode 字符串發送到帶有iostream 的控制台,但失敗了。

我發現了這個: 在 c++ 控制台應用程序中使用 unicode 字體並且此代碼段有效。

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

但是,我沒有找到使用 iostreams 正確訪問 output unicode 的任何方法。 有什么建議么?

這不起作用:

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

編輯除了將此代碼段包裝在 stream 中之外,我找不到任何其他解決方案。希望有人有更好的主意。

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

我在這里使用 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

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。 我創建了一個空白的控制台應用程序。 默認設置沒問題。 但是,如果您遇到問題或使用不同的 ide,您可能需要檢查這些:

在您的項目屬性中找到配置屬性 -> 常規 -> 項目默認值 -> 字符集。 它應該是“使用 Unicode 字符集”而不是“多字節”。 這將為您定義_UNICODEUNICODE預處理器宏。

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

另外我認為我們應該使用wmain函數而不是main 它們都可以工作,但在 unicode 環境中wmain可能更方便。

我的源文件也是 UTF-16-LE 編碼的,這似乎是 Visual Studio 2017 中的默認值。

2. 控制台代碼頁

這是很明顯的。 我們需要控制台中的 unicode 代碼頁。 如果你想檢查你的默認代碼頁,只需打開一個控制台並輸入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");

返回值很有趣。 它將包含一個字符串來准確描述選擇的語言環境。 試一試吧:-) 我用chinesegerman測試過。 更多信息: setlocale

5.使用寬字符輸出

這里不多說。 如果要輸出寬字符,請使用以下示例:

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

哦,不要忘記寬字符的L前綴! 如果您在源文件中鍵入這樣的文字 unicode 字符,則源文件必須是 unicode 編碼的。 就像 Visual Studio 中的默認值是 UTF-16-LE。 或者也許使用記事本++並將編碼設置為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;
}

干杯!

SetConsoleCP() 和chcp不一樣!

以這個程序片段為例:

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

BOM (Byte Order Mark; Signature).源代碼必須保存為BOM(字節順序標記;簽名)的 UTF-8。 然后,Microsoft 編譯器cl.exe按原樣采用 UTF-8 字符串。
BOM, cl.exe transcodes the string to ANSI (ie CP1252), which doesn't match to CP65001 (= UTF-8).如果此代碼BOM 一起保存,cl.exe 會將字符串轉碼為 ANSI(即 CP1252),這與 CP65001 (= UTF-8) 不匹配。

將顯示字體更改為Lucidia Console ,否則,UTF-8 輸出將根本無法工作。

  • 類型: chcp
  • 答案: 850
  • 類型: test.exe
  • 答案: tr├ñnen├╝berstr├ÂmtÔäó
  • 類型: chcp
  • 答案: 65001
  • 類型: chcp 65001
  • 類型: test.exe
  • 答案: tränenüberströmt™

測試:德國 Windows XP SP3

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

我剛剛測試了它,它在這里顯示的字符串絕對沒問題。

我在希伯來語中使用了一個回文詞,因為控制台應用程序可能會顛倒顯示從右到左的字符串。

這是我的多平台代碼:

#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 - Windows 特定代碼的預處理器指令

#B - 包含用於低級 I/O 操作的 io.h 庫

#C - 包含用於文件控制操作的 fcntl.h 庫

#D - 非 Windows 代碼的預處理器指令

#E - 包含用於特定於語言環境的操作的語言環境庫

#F - 將 stdout 的模式設置為使用 Unicode

#G - 將希伯來語單詞打印到控制台

#H - 將全局語言環境設置為用戶的首選語言環境

#I - 將 wcout 的語言環境設置為全局語言環境

您可以使用開源 {fmt} 庫來便攜地打印 Unicode 文本,包括在 Windows 上,例如:

#include <fmt/core.h>

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

輸出:

èéøÞǽлљΣæča

這需要使用 MSVC 中的/utf-8編譯器選項進行編譯。

我不建議使用wcout因為它是不可移植的,甚至在沒有額外努力的情況下也無法在 Windows 上運行,例如:

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

將打印:

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

在俄語 Windows 中(ACP 1251,控制台 CP 866)。

免責聲明:我是 {fmt} 的作者。

如果您正在尋找可移植的解決方案,但不幸的是,它仍然不是 C++20 標准的一部分,我可以推薦nowide庫。 它可以是獨立的,也可以作為 boost 的一部分。 您會發現許多標准對應物在那里使用或發出 utf-8 編碼的char 是的, char s,而不是char8_t s(還)。 隨意使用char8_t-remediation 實用程序char8_t s 解釋為char s,如果您的程序已經運行它們。

請求的代碼片段如下所示:

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

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

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

注意:請注意流方向問題 在我的回答上下文中,一個簡短的建議是:對輸入/輸出和 utf-8 編碼數據專門使用nowide

我認為沒有一個簡單的答案。 查看控制台代碼頁SetConsoleCP 函數,您似乎需要為要輸出的字符集設置適當的代碼頁。

最近我想將 unicode 從 Python 流式傳輸到 Windows 控制台,這是我需要做的最低要求:

  • 您應該將控制台字體設置為覆蓋 unicode 符號的字體。 沒有廣泛的選擇:控制台屬性 > 字體 > Lucida Console
  • 您應該更改當前的控制台代碼頁:在控制台中運行chcp 65001或使用 C++ 代碼中的相應方法
  • 使用 WriteConsoleW 寫入控制台

在 Windows 控制台上瀏覽一篇關於java unicode的有趣文章

此外,在這種情況下,在 Python 中您不能寫入默認的 sys.stdout,您需要使用 os.write(1, binarystring) 或直接調用 WriteConsoleW 的包裝器來替換它。 似乎在 C++ 中你需要做同樣的事情。

首先,對不起,我可能沒有所需的字體,所以我還不能測試它。

這里的東西看起來有點可疑

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

mswcrt 和 io 流存在一些問題。

  1. 技巧_setmode(_fileno(stdout), _O_U16TEXT); 僅適用於 MS VC++ 而不是 MinGW-GCC。 此外,有時它會導致崩潰,具體取決於 Windows 配置。
  2. SetConsoleCP(65001) 用於 UTF-8。 在許多多字節字符場景中可能會失敗,但對於 UTF-16LE 總是可以的
  3. 您需要在應用程序退出時恢復預覽控制台代碼頁。

Windows 控制台在 UTF-16LE 模式下通過 ReadConsole 和 WriteConsole 函數支持 UNICODE。 背景效果 - 在這種情況下管道將不起作用。 即 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

截屏:

默認編碼:

  • 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:: w string 到 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::cout 直接打印 std::string。
如果你有 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 打印 unicode 字符。

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

}

窗口代碼。 使用 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 上進行測試。

  • 使用 VC++ 在 Microsoft Visual Studio 2017 上測試; 標准=c++17。 (Windows 項目)
  • 在 Microsoft Visual Studio 2017 上使用 g++ 進行測試; 標准=c++17。 (Linux 項目)
  • 在 Jetbrains Clion 2018.3 上使用 g++ 進行測試; 標准=c++17。 (Linux 工具鏈/遠程)

質量保證

問:為什么不使用<codecvt>頭函數和類?
A.棄用刪除或棄用的功能不可能在 VC++ 上構建,但在 g++ 上沒有問題。 我更喜歡 0 警告和頭痛。

Q. Windows 上的 wstring 是互通的。
A.棄用刪除或棄用的功能不可能在 VC++ 上構建,但在 g++ 上沒有問題。 我更喜歡 0 警告和頭痛。

問: 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 元素中,不需要代理對。 檢查UNIX、Linux 和 Windows 上的標准數據類型

問: std ::string 是跨平台的嗎?
答:是的。 std::string 使用字符元素。 保證 char 類型在所有編譯器中具有相同的字節大小。 char 類型大小為 1 個字節。 檢查UNIX、Linux 和 Windows 上的標准數據類型

在具有英國區域設置的 Win10 下從 VS2017 運行控制台應用程序需要我:

  1. 設置VS2017工具>環境>字體和顏色>字體:例如'Lucida'
  2. 使用編碼“Unicode(帶簽名的 UTF-8)-代碼頁 650001”保存 C++ 源文件,以便您可以在沒有編譯器警告的情況下輸入重音字符 L"âéïôù",同時避免在任何地方使用雙字節字符
  3. 使用 Configuration Properties > General > CharacterSet > "Use Multi-byte.." 和 Configuration Properties > C/C++ > All Options > Additional Options > "/utf-8" 標志編譯
  4. #include <iostream>、<io.h> 和 <fcntl.h>
  5. 執行一個晦澀的 '_setmode(_fileno(stdout), _O_WTEXT);' 一次在應用程序開始時
  6. 忘記 'cout <<... ;' 並且只使用 'wcout << ... ;'

備注,Win7 上的 VS2015 需要一個“SetConsoleOutputCP(65001);” 並允許通過 wcout 和 cout 混合輸出。

在我的情況下,我正在閱讀 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;

方案一:使用WCHAR

一件事總是有效:在所有地方使用寬字符。 喜歡,

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

Unicode 是語言中立的。 您可以使用任何語言並且不會有編碼問題。 你想使用 UTF-8 嗎? 美好的。 首先使用 MultiByteToWideChar 將其轉換為寬字符字符串。

在繼續閱讀下面的其他解決方案之前,請注意這個解決方案有一個獨特的優勢:它不依賴於系統或用戶的區域設置。

解決方案2:正確設置系統區域和用戶區域。 他們應該是一樣的。

我假設 Windows 的 UTF-8 語言環境還沒有出現在圖片中。 然后您需要知道您將使用哪種語言(中文、法語?),並更改您的系統設置以匹配它。 有系統級設置: 更改系統代碼頁

以及用戶級別設置: 在此處輸入圖片說明

請將它們設置為相同的語言。

然后,在您的程序中,插入“setlocale(LC_ALL, "");” 到您的主要功能。 這是一個通用規則,無論您使用哪種操作系統,每當您想使用標准庫來處理 ASCII 以外的字符集時,您都應該擁有這行代碼。 否則,語言環境默認為“C”並且它只包含 ASCII。 然后你就可以開始使用 std::wcout 和像 fputws 這樣的 C 函數了。

在 Win10 上使用帶有 UNICODE 控制台應用程序的 VS2019 進行測試發現以下測試西班牙語和日語:

如果你只是wprintf一個字符串,那么你會得到錯誤的西班牙語字符(日語未測試但確定它不會工作)。 看起來默認的“C”語言環境默認是 ASCII(PC 的傳統擴展 ASCII 整理表)。

使用: setlocale(LC_ALL, ""); 使用西班牙語(墨西哥) Windows 語言設置時將正確的代碼頁設置為 CP1252,並且 output 很好(lucida 控制台字體)。 但是,日語 output(使用日語Windows 語言)被抑制(意味着這些字符沒有 output,輸出正常的拉丁字符)。

使用:'_setmode(_fileno(stdout), _O_U16TEXT);` output 適用於所有人。 但是,所有 output 都是 16 位的,因此重定向到文件會輸出 16 位字符。

使用: printfUTF-8和 8834335060888 文本 output 和SetConsoleOutputCP(CP_UTF8)也有效(但如果您在setlocale(LC_ALL, ""); -我必須刪除它才能使 output 正常工作)。

Fonts :對於亞洲字符,請使用 MS Mincho,對於其他字符,您可以使用 Lucida Console。

在 Windows 控制台中正確顯示西歐字符

長話短說:

  1. 使用chcp查找適合您的代碼頁。 就我而言,它是西歐的chcp 28591
  2. 可選擇將其REG ADD HKCU\\Console /v CodePage /t REG_DWORD /d 28591默認值: REG ADD HKCU\\Console /v CodePage /t REG_DWORD /d 28591

發現歷史

我有一個類似的問題,Java。 它只是裝飾性的,因為它涉及發送到控制台的日志行; 但它仍然很煩人。

我們的 Java 應用程序的輸出應該是 UTF-8 格式,並且它在 eclipse 的控制台中正確顯示。 但在 windows 控制台中,它只顯示 ASCII 框繪圖字符: Inicializaci├│nart├¡culos而不是Inicializaciónartículos

我偶然發現了一個相關的問題,並混合了一些答案以獲得對我有用的解決方案。 解決方案是更改控制台使用的代碼頁使用支持 UNICODE 的字體(如consolaslucida console )。 Windows cosole的系統菜單中可以選擇的字體:

  1. 通過以下任一方式啟動控制台
    • Win + R然后輸入cmd並按Return鍵。
    • Win鍵並輸入cmd后跟return鍵。
  2. 通過以下任一方式打開系統菜單
    • 單擊左上角圖標
    • Alt + Space組合鍵
  3. 然后選擇“默認”以更改所有后續控制台窗口的行為
  4. 單擊“字體”選項卡
  5. 選擇ConsolasLucida 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 \\控制台注冊表項中的數據。 這確實對我有用。

請注意 HKCU ("HKEY_CURRENT_USER") 僅適用於當前用戶。 如果您想為該計算機中的所有用戶更改它,您需要使用regedit實用程序並找到/創建相應的Console鍵(可能您必須在HKEY_USERS\\.DEFAULT創建一個Console鍵)

我有一個類似的問題, 使用 C++ 將 Unicode 輸出到控制台,在 Windows 中包含在運行程序之前需要在控制台中執行chcp 65001的 gem。

可能有一些以編程方式執行此操作的方法,但我不知道它是什么。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM