wxWidgets源碼分析(9) - wxString

來源:互聯網
上載者:User

標籤: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的內建函式AsCharmb_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調用AsCharBufAsCharBuf接著調用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類的介面定義:

  1. MB2WC:用於實現從multibyte encoding 到 Unicode的轉換,也就是GBK到UTF-16的轉換;具體的實現就是調用win32API MultiByteToWideChar 函數執行字元集的轉換
  2. WC2MB:與上一個相反,用於實現 Unicode 到 multibyte encoding的轉換;具體的實現是調用 WideCharToMultiByte 函數執行字元集的轉換。

對於Unix平台,實際上時調用的mbstowcs()wcstombs()實現的轉換,這兩個函數是標準C庫函數,這兩個函數的實現與當前系統的locale息息相關,手冊上明確說明這兩個函數的行為依賴 LC_CTYPE 值,至於具體的實現,可以參考locale相關的文檔。

wxWidgets源碼分析(9) - wxString

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.