1. 程式人生 > >用boost locale庫進行字符集轉換的問題

用boost locale庫進行字符集轉換的問題

本想用 boost::locale::conv::to_utf<wchar_t> 寫一個將 std::string 轉換為 std::wstirng 的函式以達到簡化介面形式,並且程式碼可以跨平臺的目的。

然而 MSVC 2010  在 Windows 10上測試時遇到了問題。 以下是測試程式碼說明情況。

int _tmain(int argc, _TCHAR *argv[])
{
  try {
    std::locale loc = boost::locale::generator().generate("" );

    std::wstring a = L"1: Five Chinese words[白日依山盡]_by macro L";
    std::wstring b = boost::locale::conv::to_utf<wchar_t>( "2: Five Chinese words[黃河入海流]_by boost to_utf", loc ); 
    std::wstring c = L"3: Five Chinese words[欲窮千里目]_by macro L"; 

    std::wcout.imbue( std::locale("") );
    std::wcout << a <<  std::endl;
    std::wcout << b <<  std::endl;
    std::wcout << c <<  std::endl;

    std::cout << "\n  charset of std::locale(\"\") is " 
              << std::use_facet<boost::locale::info>(loc).encoding()
              << std::endl;

  } catch ( std::exception &e ) {
    std::cout << e.what() << std::endl;
  }
}

本期待輸出四行,

1: Five Chinese words[白日依山盡]_by macro L
2: Five Chinese words[黃河入海流]_by boost to_utf
3: Five Chinese words[欲窮千里目]_by macro L

  charset of std::locale("") is cp936

實際結果是:
1: Five Chinese words[白日依山盡]_by macro L
2: Five Chinese words[
  charset of std::locale("") is utf-8

第一行,正確;

第二行,從中文開始,沒有顯示;
第三行,沒有顯示;

第四行,顯示系統預設字符集是 utf-8.    ( 實際輸出的第三行)

可見從第二行的中文開始,wcout 就不能正常工作了。這與 boost 的 to_utf 函式說明不符。

第四行的結果“utf-8”說明了問題的原因。即由 boost::locale::generator().generate("" ) 產生的 std::locale loc,其中的字符集編碼是 utf-8 而不是 Windows 系統中實際使用內碼表 cp936 ( 對應字符集 GBK)。
但是,to_utf 只能接受由 boost::locale::generator 生成的 locale, 而不能直接接受 std::loale(""), 後者將導致 "bad_cast" 異常。因此,在windows下只能使用 to_utf 的第二種形式,即直接提供字符集名稱來進行轉換。
to_utf 在 Linux 下的行為則符合預期。

基於以上原因,不得不將 Windows 和 Linux 區別對待。在 windows 下使用字符集名稱的方式,而在 Linux 下使用 boost 的標準寫法。一個完整的例子如下:

#ifdef WIN32
#include <windows.h>
#endif

#include <boost/locale.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>
#include <iostream>

#pragma comment(lib,"libboost_thread-vc100-mt-gd-1_55.lib")
#pragma comment(lib,"libboost_system-vc100-mt-gd-1_55.lib")

std::wstring toutf( std::string & src )
{
  #ifdef WIN32
      static std::string codepage;
    if ( codepage.empty() ) {
      // 獲得系統當前的內碼表。對於中文Windows, 
      CPINFOEX  cpinfo;
      GetCPInfoEx( CP_ACP, 0, &cpinfo );
      cpinfo.CodePageName;
      codepage = "CP" + boost::lexical_cast<std::string>(cpinfo.CodePage);
    }
    
    std::wstring dst = boost::locale::conv::to_utf<wchar_t>( src, codepage.c_str() );
  #else
    std::locale loc = boost::locale::generator().generate("");
    std::wstring dst = boost::locale::conv::to_utf<wchar_t>( src, loc );
  #endif
  
  return dst;
}

int main(int argc, char *argv[])
{
  try {
    std::locale loc = boost::locale::generator().generate("" );

    std::wstring a = L"1: Five Chinese words[白日依山盡]_by macro L";
    std::wstring b = toutf( std::string("2: Five Chinese words[黃河入海流]_by boost to_utf") ); 
    std::wstring c = L"3: Five Chinese words[欲窮千里目]_by macro L"; 

    std::wcout.imbue( std::locale("") );
    std::wcout << a <<  std::endl;
    std::wcout << b <<  std::endl;
    std::wcout << c <<  std::endl;

    std::cout << "\n  charset of std::locale(\"\") is " 
      << std::use_facet<boost::locale::info>(loc).encoding()
      << std::endl;

  } catch ( std::exception &e ) {
    std::cout << e.what() << std::endl;
  }
}

輸出的結果是:
1: Five Chinese words[白日依山盡]_by macro L
2: Five Chinese words[黃河入海流]_by boost to_utf
3: Five Chinese words[欲窮千里目]_by macro L

  charset of std::locale("") is utf-8