標籤:windows平台 寫代碼 ali 必須 字元轉換 靜態 建議 模式 debug模式
目錄
- wxString
- wxString的中文字元支援
- wxString與通用字串的轉換
- 字元集轉換
wxStringwxString的中文字元支援
中文字元的編碼格式如下:
| 漢字 |
GBK |
區位碼 |
UTF-8 |
UTF-16 |
| 中 |
D6 D0 |
54 48 |
E4 B8 AD |
4E 2D |
| 文 |
CE C4 |
46 36 |
E6 96 87 |
65 87 |
不同作業系統的預設內碼
Windows系統(預設GBK):41 42 d6 d0 ce c4Linux系統(預設UTF-8):41 42 e4 b8 ad e6 96 87
Windows
wxStrubg在window系統中使用了UTF-16編碼格式。但是在windows系統中,預設的檔案的編碼格式為GBK格式,考慮到這點,wxString在對賦值取值時,對字元編碼進行了從GBK到UTF-16編碼的轉換,這也就要求源碼必須是GBK格式的!!,否則顯示出來的就是亂碼,當然還有另外一條路,就是使用wxConv類告訴wxString當前的字元編碼是什麼格式的。
源碼檔案格式為GBK時的測試,可以發現只有不進行轉換的才可以顯示。
測試函數:wxString("AB中文",wxConvUTF8)輸出:空測試函數:wxString("AB中文")輸出:41 42 4E2D 6587 測試函數:_("AB中文")輸出:41 42 4E2D 6587
源碼檔案格式為UTF-8時的測試,可以發現只有轉換的才可以顯示,其他的都顯示亂碼。
測試函數:wxString("AB中文",wxConvUTF8)輸出:41 42 4E2D 6587 測試函數:wxString("AB中文")輸出:41 42 6D93 E15F 6783 測試函數:_("AB中文")輸出:41 42 6D93 E15F 6783
Linux Unicode
編譯wxWidgets 3.0 時指定了“--enable-unicode”選項,指定此選項後wxString的內部編碼使用的是UTF-16格式進行編碼(註:即使不指定--enable-unicode選項,wxWidgets依舊使用unicode格式儲存資料,Windows系統中使用UTF-16,Linux&MAC系統中使用UTF-32)
測試函數:wxString("AB中文",wxConvUTF8)輸出:41 42 4E2D 6587 測試函數:wxString("AB中文")輸出:空測試函數:_("AB中文")輸出:空(源碼檔案格式為UTF-8,如果源檔案為非UTF-8格式,則都輸出錯誤)
可以看出,Linux系統中檔案格式是 UTF-8 格式的,轉換後,wxString內部是UTF-32格式的,如果要在Linux中顯示中文,則需要使用wxConvUTF8進行轉換。
Linux UTF-8
編譯wxWidgets 3.0 時指定了“--enable-utf8 --enable-utf8only”選項,這樣在wxString內部編碼時就直接使用UTF-8格式了,增加 “--enable-utf8only”選項後,則禁止了字元之間的轉換,在效率上有大幅的提升,同時編寫代碼也容易多了:
測試函數:wxString("AB中文",wxConvUTF8)輸出:41 42 4E2D 6587 測試函數:wxString("AB中文")輸出:41 42 4E2D 6587 測試函數:_("AB中文")輸出:41 42 4E2D 6587 (源碼檔案格式為UTF-8,如果源檔案為非UTF-8格式,則都輸出錯誤)
注意到,上面三種格式的資料都能正常輸出。
總結
建議後續在寫wxWidgets程式時,介面中涉及到的語言全部是使用英語,這樣不管檔案是GBK編碼也好,是UTF-8編碼也好,都能夠編譯、顯示正常。
如果需要支援多過語言,建議使用wxWidgets提供的wxLocale解決方案。
wxString與通用字串的轉換wxString對象的建立
除了wxString的建構函式外,可以通過以下幾種方式來產生wxString對象:
| 靜態方法 |
說明 |
| wxString::FromAscii() |
通過ASCII碼的字串來建立wxString對象,如果字串中含有大於等於0x80的字串,wxString會報錯,比如
wxString str = wxString::FromAscii("ABC中文"); 在運行時如果開啟了debug模式就會收到alert資訊,同時顯示該字串如果顯示出來是亂碼; |
| wxString::FromUTF8() |
通過UTF-8字串來建立對象,比如:
wxString test = wxString::FromUTF8("\xF0\x90\x8C\x80"); |
| 建構函式+wxMBConv對象 |
wxMBConv對象預設值為wxConvLibc |
將wxString對象轉換為其他類型資料
wxString 提供了多種方法,可以將資料轉換為你需要的資料;
c_str()
原型:
wxCStrData wxString::c_str() const;
本函數用於將wxString對象轉換為 const char* 或者 const wchar_t* 格式的資料,以下為使用方法:
const char *p = str.c_str(); // 左側已經註明類型,c_str函數將會輸出`const char*`類型sprintf(abStr, "sprintf = %s\n", (const char *)str.c_str()); // 必須加強轉
跟蹤代碼可以看到wxCStrData是通過重載強轉操作符實現的類型自動轉換:
inline const wchar_t* AsWChar() const; inline const char* AsChar() const; const unsigned char* AsUnsignedChar() const { return (const unsigned char *) AsChar(); } // 重載轉換操作符 operator const wchar_t*() const { return AsWChar(); } operator const char*() const { return AsChar(); } operator const unsigned char*() const { return AsUnsignedChar(); } operator const void*() const { return AsChar(); }
資料的輸出通過的是 wxCStrData::AsChar() 函數,內部實現實際上時通過調用wxString的內建函式AsChar和mb_str來實現的:
inline const char* wxCStrData::AsChar() const{#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY const char * const p = m_str->AsChar(wxConvLibc); if ( !p ) return "";#else // !wxUSE_UNICODE || wxUSE_UTF8_LOCALE_ONLY const char * const p = m_str->mb_str();#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY return p + m_offset;}
mb_str()
原型:
const wxCharBuffer mb_str (const wxMBConv &conv=wxConvLibc) const;
使用方法與c_str類似:
const char *p = str.c_str();
函數的實現如下,mb_str調用AsCharBuf,AsCharBuf接著調用AsChar實現轉換(AsChar函數後續有詳細分析):
const wxScopedCharBuffer mb_str(const wxMBConv& conv = wxConvLibc) const{ return AsCharBuf(conv);}wxScopedCharBuffer AsCharBuf(const wxMBConv& conv) const{ ... if ( !AsChar(conv) ) ...}
我們再看下wxScopedCharBuffer的實現,它也應當同樣實現了wxCStrData函數的操作符強制轉換,跟蹤代碼:
typedef wxScopedCharTypeBuffer<char> wxScopedCharBuffer;// 繼續 wxScopedCharTypeBuffer,可以看到它也實現了操作符重載,轉換為模板指定的類型// 對於 wxScopedCharBuffer 來說就是 const char*template <typename T>class wxScopedCharTypeBuffer{public: typedef T CharType; const CharType *data() const { return m_data->Get(); } operator const CharType *() const { return data(); }}
utf8_str()
函數返回UTF-8字串,可以看到,它是通過調用mb_str(wxMBConvUTF8())實現了自身的轉換,輸出的字串格式為UTF-8編碼的。
const wxScopedCharBuffer utf8_str() const { return mb_str(wxMBConvUTF8()); }
ToStdString()
用於將wxString對象轉換稱std::string對象,可以看到,最終還是通過調用mb_str函數進行轉換。
std::string ToStdString() const{ wxScopedCharBuffer buf(mb_str()); return std::string(buf.data(), buf.length());}
wxString::AsChar轉換的關鍵函數
下面針對Unicode模式的程式碼分析:
const char *wxString::AsChar(const wxMBConv& conv) const{#if wxUSE_UNICODE_UTF8 ...#else // wxUSE_UNICODE_WCHAR // 調用c_str()擷取wchar_t*,由於wxString內部就是使用wchar_t*儲存的 // 所以這步調用直接擷取到內部的buffer const wchar_t * const strWC = m_impl.c_str(); const size_t lenWC = m_impl.length();#endif // wxUSE_UNICODE_UTF8/wxUSE_UNICODE_WCHAR // 調用`conv.FromWChar`將Unicode字元轉換為當天系統的MultiByte字串 const size_t lenMB = conv.FromWChar(NULL, 0, strWC, lenWC); if ( lenMB == wxCONV_FAILED ) return NULL; if ( !m_convertedToChar.m_str || lenMB != m_convertedToChar.m_len ) { if ( !const_cast<wxString *>(this)->m_convertedToChar.Extend(lenMB) ) return NULL; } m_convertedToChar.m_str[lenMB] = ‘\0‘; if ( conv.FromWChar(m_convertedToChar.m_str, lenMB, strWC, lenWC) == wxCONV_FAILED ) return NULL; return m_convertedToChar.m_str;}
調用 wxMBConv::FromWChar 進行字串轉換, 跟蹤 wxMBConv::FromWChar 的實現,它會繼續調用wxMBConv::WC2MB執行轉換,可參考另外一片文章,有描述此函數的實現。
通過上述過程實現了wxString到本地字串的轉換。
字元集轉換
正式代碼中通過下面的方式調用(wxWidgets-3.0.2):
wxString testStr("abc中文測試");
接著我們看下wxString內部時如何?的:
調用wxString的建構函式:
// string.h L1241 wxString(const char *psz) : m_impl(ImplStr(psz)) {}
wxString的建構函式調用ImplStr來初始化wxString的內部變數m_impl,函數定義如下:
static wxScopedWCharBuffer ImplStr(const char* str, const wxMBConv& conv = wxConvLibc) { return ConvertStr(str, npos, conv).data; }
我們先看下第二個參數wxConvLibc的由來,具體實現我們後面再說:
// strconv.h L563#define WX_DECLARE_GLOBAL_CONV(klass, name) extern WXDLLIMPEXP_DATA_BASE(klass*) name##Ptr; extern WXDLLIMPEXP_BASE klass* wxGet_##name##Ptr(); inline klass& wxGet_##name() { if ( !name##Ptr ) name##Ptr = wxGet_##name##Ptr(); return *name##Ptr; } // 使用WX_DECLARE_GLOBAL_CONV宏預定義轉換對象,實際是聲明了一個函數// 和一個指標,實現wxGet_wxConvLibc函數返回這個指標,保證全域唯一WX_DECLARE_GLOBAL_CONV(wxMBConv, wxConvLibc)#define wxConvLibc wxGet_wxConvLibc()
接著 ImplStr 會調用 ConvertStr 來進行轉換,函數實現如下:
// string.cpp L385#if wxUSE_UNICODE_WCHARwxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, const wxMBConv& conv){ // 調用 conv.cMB2WC 進行轉換 wxScopedWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen));}
繼續調用 wxMBConv::cMB2WC 進行字元資料的轉換,我們跟蹤一下源碼,此函數會調用 wxMBConv::ToWChar 進行資料轉換,然後返回長度:
const wxWCharBufferwxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const{ const size_t dstLen = ToWChar(NULL, 0, inBuff, inLen); //...
查看wxMBConv::ToWChar的源碼可以看到,它繼續調用MB2WC方法進行字元的轉換,這個是個虛介面,接著我們看看這個虛介面的實現。
接著上文的 wxConvLib ,我們看看是怎麼實現的。
// strconv.cpp L3417#define WX_DEFINE_GLOBAL_CONV2(klass, impl_klass, name, ctor_args) WXDLLIMPEXP_DATA_BASE(klass*) name##Ptr = NULL; WXDLLIMPEXP_BASE klass* wxGet_##name##Ptr() { static impl_klass name##Obj ctor_args; return &name##Obj; } /* this ensures that all global converter objects are created */ /* by the time static initialization is done, i.e. before any */ /* thread is launched: */ static klass* gs_##name##instance = wxGet_##name##Ptr()// strconv.cpp L3437#ifdef __WINDOWS__ WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConv_win32, wxConvLibc, wxEMPTY_PARAMETER_VALUE);#elif 0 // defined(__WXOSX__) WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConv_cf, wxConvLibc, (wxFONTENCODING_UTF8));#else WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConvLibc, wxConvLibc, wxEMPTY_PARAMETER_VALUE);#endif
上面的宏展開後,可以得到在windows平台上 wxConvLibc 的實作類別是 wxMBConv_win32,在其他平台上實作類別是 wxMBConvLibc。
對於windows平台中wxMBConv_win32實現可參考 strconv.cpp 中的實現,主要實現了 MB2WC 和 WC2MB 兩個介面,功能說明可以參考wxMBConv類的介面定義:
- MB2WC:用於實現從multibyte encoding 到 Unicode的轉換,也就是GBK到UTF-16的轉換;具體的實現就是調用win32API MultiByteToWideChar 函數執行字元集的轉換
- WC2MB:與上一個相反,用於實現 Unicode 到 multibyte encoding的轉換;具體的實現是調用 WideCharToMultiByte 函數執行字元集的轉換。
對於Unix平台,實際上時調用的mbstowcs()和wcstombs()實現的轉換,這兩個函數是標準C庫函數,這兩個函數的實現與當前系統的locale息息相關,手冊上明確說明這兩個函數的行為依賴 LC_CTYPE 值,至於具體的實現,可以參考locale相關的文檔。
wxWidgets源碼分析(9) - wxString