2013-04-05 165 views
8

如何在C++中設置編碼最好?在C++中設置編碼的最正確方法是什麼?

我習慣使用Unicode(和wchar_t,wstring,wcin,wcout和L「...」)。我也用UTF-8保存源文件。

目前我使用MinGW(Windows 7)並在Windows控制檯(cmd.exe)中運行我的程序,但有時我可以在GNU \ Linux上使用gcc,並在使用UTF-8編碼的Linux控制檯中運行promgram。

在任何時候我都想在Windows和Linux上編譯我的源代碼,並且我希望所有Unicode符號都能正確輸入和輸出。

當我遇到下一個編碼問題時,我用Google搜索了一下。此外,我發現的最不同的委員會:setlocale(LC_ALL, "")setlocale(LC_ALL, "xx_XX.UTF-8")std::setlocale(LC_ALL, "")std::setlocale(LC_ALL, "xx_XX.UTF-8")<clocale>

SetConsoleCP()SetConsoleOutputCP()<windows.h>和許多其他人。

最後我被這個薩滿教所困擾,我想問你:如何正確地建立編碼?

+0

你想改變什麼?你想改變線程語言環境嗎?系統區域設置? UI語言?還是活動的代碼頁?對於線程,控制檯或系統?有很多選項,遠遠超過單個'setlocale'函數所暗示的選項。你必須解釋你想看到的*效果,然後才能告訴你什麼開關可以翻轉。 – 2013-04-05 05:26:16

+0

@CodyGray,我需要任何Unicode符號/字符串正確輸入和outputed。這個_effect_的充分描述?我想,這意味着我需要更改程序啓動的控制檯的編碼。 – 2013-04-05 15:09:54

+0

通常,我會說一個程序不應該修改語言環境 - 它應該在提供的語言環境中工作。否則,這種做法有悖於「國際化」的目的。 – 2013-04-06 00:51:45

回答

5

我需要任何Unicode符號/字符串被正確輸入和輸出。

這當然是可能的,雖然使Windows命令提示符控制檯正確識別Unicode需要一些特殊的魔力。不幸的是,我嚴重懷疑標準庫函數的任何實現都會這樣做。

在Stack Overflow上你會發現很多關於它的問題,但是this one is a good one。基本上,控制檯默認使用所謂的(有些錯誤地)「OEM」代碼頁。您想要將其更改爲UTF-8代碼頁,其值由CP_UTF8定義。爲此,您需要同時調用SetConsoleCP函數(設置輸入代碼頁)和SetConsoleOutputCP函數(設置輸出代碼頁)。該守則將是這個樣子:

if (!SetConsoleCP(CP_UTF8)) 
{ 
    // An error occurred; handle it. Call GetLastError() for more information. 
    // ... 
} 
if (!SetConsoleOutputCP(CP_UTF8)) 
{ 
    // An error occurred; handle it. Call GetLastError() for more information. 
    // ... 
} 

對於額外的耐用性,你可能還需要確保的是,UTF-8的代碼頁是第一個支持,試圖設置和使用它之前。你可以通過調用IsValidCodePage函數來做到這一點。例如:

if (IsValidCodePage(CP_UTF8)) 
{ 
    // We're all good, so set the console code page... 
} 

您還必須將字體從包含必要的Unicode字符字形-e.g,龍力控制檯或索拉(reference)的默認(「點陣字體」)的東西改變。使用SetCurrentConsoleFontEx函數這是微不足道的。

不幸的是,此功能在Vista之前的Windows版本中不存在。如果你絕對需要支持這些較老的操作系統,我唯一能做的就是調用無證的SetConsoleFont函數。通常,我建議強烈反對使用無證函數,但我認爲這不是一個問題,因爲你會只有在舊版本的操作系統中使用它。你知道這些不會改變。在可用的新版本中,您可以調用支持的功能。未經測試的代碼示例:

bool IsWinVistaOrLater() 
{ 
    OSVERSIONINFOEX osvi; 
    osvi.dwOSVersionInfoSize = sizeof(osvi); 
    GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi)); 

    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) 
    { 
     return osvi.dwMajorVersion >= 6; 
    } 
    return false; 
} 

void SetConsoleToUnicodeFont() 
{ 
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 
    if (IsWinVistaOrLater()) 
    { 
     // Call the documented function. 
     typedef BOOL (WINAPI * pfSetCurrentConsoleFontEx)(HANDLE, BOOL, PCONSOLE_FONT_INFOEX); 
     HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll")); 
     pfSetCurrentConsoleFontEx pfSCCFX = (pfSetCurrentConsoleFontEx)GetProcAddress(hMod, "SetCurrentConsoleFontEx"); 

     CONSOLE_FONT_INFOEX cfix; 
     cfix.cbSize  = sizeof(cfix); 
     cfix.nFont  = 12; 
     cfix.dwFontSize.X = 8; 
     cfix.dwFontSize.Y = 14; 
     cfix.FontFamily = FF_DONTCARE; 
     cfix.FontWeight = 400; // normal weight 
     lstrcpy(cfix.FaceName, TEXT("Lucida Console")); 

     pfSCCFX(hConsole, 
       FALSE, /* set font for current window size */ 
       &cfix); 
    } 
    else 
    { 
     // There is no supported function on these older versions, 
     // so we have to call the undocumented one. 
     typedef BOOL (WINAPI * pfSetConsoleFont)(HANDLE, DWORD); 
     HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll")); 
     pfSetConsoleFont pfSCF = (pfSetConsoleFont)GetProcAddress(hMod, "SetConsoleFont"); 
     pfSCF(hConsole, 12); 
    } 
} 

請注意,我已經爲閱讀者添加了所需的錯誤檢查。這裏的重點是技術和可讀性;把它與錯誤處理混在一起只會混淆事項。

我不知道如何在Linux上做這些。我懷疑它的工作量要少很多,因爲人們告訴我操作系統在內部使用UTF-8。無論哪種方式,你都是你自己的;使Windows咕嚕聲足夠工作的一個答案!

0

我剛剛需要輸出Unicode文本到控制檯,只有這個功能WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...);幫助。對於輸入,我假設ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), ...);有竅門。

PSWriteOutput在輸出字符串大小上有限制。因此,如果時間更長,您可能需要將其重新分塊。

相關問題