1. 程式人生 > >[轉自周星星的blog] 解決為什麼wcout不能輸出中文問題

[轉自周星星的blog] 解決為什麼wcout不能輸出中文問題

wprintf 和 wcout

這篇文章應該是[netsin]的成果,我勤快,記下來。
注:wprintf是C的標準庫函式,但wcout不是C++的標準成員,C++中的 L"……" 是寬字元,卻未必是unicode字元,這與編譯器實現相關。
[乾坤一笑]說:為什麼 C/C++ 語言把 L"xx" 定義為由實現決定的呢?這顯然是為了 C/C++ 的普適性、可移植性。Bjarne 的觀點認為,C++ 的方式是允許程式設計師使用任何字符集作為串的字元型別。另外,unicode 編碼已經發展了若干版本了,是否能永久適合下去也不得而知。有關 unicode 的詳細論述以及和其它字符集的比較,我推薦你看《無廢話xml》。


以下兩段程式碼的執行環境是 windows xp professional 英文版,編譯器是 VS2005RTM。

// C
#include <stdio.h>
#include <locale.h>
int main( void )
{
    setlocale( LC_ALL, "chs" );
    //setlocale( LC_ALL, "Chinese-simplified" );
    //setlocale( LC_ALL, "ZHI" );
    //setlocale( LC_ALL, ".936" );
    wprintf( L"中國" );

    return 0;
}

// C++
#include <iostream>
#include <locale>
using namespace std;
int main( void )
{
    locale loc( "chs" );
    //locale loc( "Chinese-simplified" );
    //locale loc( "ZHI" );
    //locale loc( ".936" );
    wcout.imbue( loc );
    std::wcout << L"中國" << endl;

    return 0;
}

說明:別混合使用 setlocale 和 std::locale 。

-------------------------

2006-07-05 記 -------------------------

 "VC知識庫"                        編碼為:56 43 D6 AA CA B6 BF E2 00                            // ANSI編碼
L"VC知識庫" 在VC++               中編碼為:56 00 43 00 E5 77 C6 8B 93 5E 00 00                   // (windows口中的unicode)編碼
L"VC知識庫" 在GCC(Dev-CPP4990) 中編碼為:56 00 43 00 D6 00 AA 00 CA 00 B6 00 BF 00 E2 00 00 00 // 只是將ANSI編碼簡單的加0
L"VC知識庫" 在GCC(Dev-CPP4992) 中編譯失敗,報 Illegal byte sequence

L"VC知識庫" 在 Dev-CPP4992 中解決步驟為:
a. 將檔案儲存為 utf-8 編碼                                          // utf-8 是unicode的其中一種,但和(windows口中的unicode)不一樣
b. 去掉BOM頭:用二進位制編輯器(比如VC)去掉剛才utf-8檔案的前三個位元組 // Linux/UNIX並不使用BOM
c. 使用 gcc/g++ 編譯執行

經過以上解決步驟,在 dev-cpp4992 中
 "VC知識庫" 編碼為: 56 43 E7 9F A5 E8 AF 86 E5 BA 93 00 // utf-8編碼,注意不再是ANSI編碼了,因此用 printf/cout 將輸出亂碼
L"VC知識庫" 編碼為: 56 00 43 00 E5 77 C6 8B 93 5E 00 00 // (windows口中的unicode)編碼

補充:在mingw32中使用wcout和wstring需要加一些巨集,比如
#define _GLIBCXX_USE_WCHAR_T 1
#include <iostream>
int main( void )
{
    std::wcout << 1 << std::endl;
}
可以編譯通過,但無法Link通過,在網上google了一下,stlport說mingw32有問題,mingw32說是M$的c runtime有問題。

------------------------- 2007-01-05 記 -------------------------
一個多位元組字串和寬字元字串互相轉化的事例
#define _CRT_SECURE_NO_WARNINGS // only for vc8
#include <string>
#include <clocale>
#include <cassert>
inline const std::string to_mbcs( const std::string& src )
{
    return src;
}
const std::string to_mbcs( const std::wstring& src )
{
    char* old_locale = _strdup( setlocale(LC_CTYPE,NULL) ); // 儲存原來的locale
    setlocale( LC_CTYPE, "chs" ); // 設定當前locale為chs,這在非簡體中文平臺上不可缺少

    size_t count1 = wcstombs( NULL, src.c_str(), 0 ); // 計算新字串長度
    std::string des( count1, ' ' );
    size_t count2 = wcstombs( &des[0], src.c_str(), count1 ); // 轉化
    assert( count1 == count2 );

    setlocale( LC_CTYPE, old_locale ); // 恢復到原來的locale
    free( old_locale );

    return des;
}
inline const std::wstring to_wcs( const std::wstring& src )
{
    return src;
}
const std::wstring to_wcs( const std::string& src )
{
    char* old_locale = _strdup( setlocale(LC_CTYPE,NULL) ); // 儲存原來的locale
    setlocale( LC_CTYPE, "chs" ); // 設定當前locale為chs,這在非簡體中文平臺上不可缺少

    size_t count1 = mbstowcs( NULL, src.c_str(), 0 ); // 計算新字串長度
    std::wstring des( count1, L' ' );
    size_t count2 = mbstowcs( &des[0], src.c_str(), count1 ); // 轉化
    assert( count1 == count2 );

    setlocale( LC_CTYPE, old_locale ); // 恢復到原來的locale
    free( old_locale );

    return des;
}

#include <iostream>
int main( void )
{
    using namespace std;

    cout << to_mbcs("你好1") << endl;
    cout << to_mbcs(L"你好2") << endl;

    const locale loc( "chs" );
    wcout.imbue( loc );
    wcout << to_wcs("你好3") << endl;
    wcout << to_wcs(L"你好4") << endl;
}

------------------------- 2008-09-03 記 -------------------------
參見 《MBCS To Unicode