ubuntu14.04中使用wxWidgets,中文無法正常顯示
最近一直在忙著把之前完成的一個wxWidgets程式從windows環境遷移到Ubuntu環境下,在遷移過程中遇到了很多奇怪的問題,有些解決了,有些還沒有解決。中文是中文程式遷移到ubuntu上最核心的問題,雖然在wxWidgets文件中說了wxString是用unicode碼儲存資料的,使用FromUTF8 方法可以順利的從UTF8格式的中文中讀取字元,但是實際經我驗證這種方法是無效的(測試環境 Ubuntu14.04 原版為英文 en_US.utf8 後來修改locale為 zh_CN.utf8).
在翻閱資料的時候我看到了這幾段化。
wxString::wxString | ( | const std::string & | str | ) |
Constructs a string from str using the using the current locale encoding to convert it to Unicode (wxConvLibc).
std::string wxString::ToStdString | ( | ) | const |
Return the string as an std::string in current locale encoding.
Note that if the conversion of (Unicode) string contents to the current locale fails, the return string will be empty. Be sure to check for this to avoid silent data loss.
Instead of using this function it's also possible to write
std::string s; ... s = std::string(wxs);but using ToStdString() may make the code more clear.
裡面最重要的部分是the current locale 並且會存在fail的情況,轉化失敗就出現了空字串,經過進一步確認,我們可以發現問題就出在這裡。
我們可以通過 wxLocale來獲取 系統的locale,但是我們卻無法獲取程式執行過程中會話的locale,這是問題之一。
另外我觀察到,不呼叫wxLocale的Init方法和呼叫了wxLocale的Init方法會產生不同的影響,在我的環境下,在不呼叫Init方法的時候 程式的locale可能是 en_US(推測),呼叫了init方法的時候locale變成了 zh_CN,中文可以正常顯示,這個可以通過wxLocale驗證。
最後我們需要確認一點,locale並不是專為wxString這個變數服務的,他是一個全域性性的變數,儲存著一些資訊用來進行字元轉化,這一般使用於本地化操作。locale的改變會影響很多庫函式的執行。
在圍繞locale進行了一些嘗試之後,我得到了如下結論:1處理字符集問題的解決方案不能依賴locale,2修改locale無法解決問題(會導致其他問題,並且部分地方中文還存在無法顯示的情況)
探索新解決方案的時候我發現wxT() 可以正確的將中文字串轉化並交給wxString儲存,雖然無法通過ToStdString 取出內部值,但是通過size方法可以拿到正確的大小。
接下來就是確認wxT是在做什麼
#define wxT | ( | string | ) |
This macro can be used with character and string literals (in other words, 'x'
or "foo"
) to automatically convert them to wide
strings in Unicode builds of wxWidgets.
This macro simply returns the value passed to it without changes in ASCII build. In fact, its definition is:
1 #ifdef UNICODE 2 # define wxT(x) L##x 3 #else // !Unicode 4 # define wxT(x) x 5 #endifNote that since wxWidgets 2.9.0 you shouldn't use wxT() anymore in your program sources (it was previously required if you wanted to support Unicode).
Include file:
#include <wx/chartype.h>官方文件中的說法表明wxT實際上是將字串在wxWidgets支援unicode的情況下轉化成寬字串
那麼我們可以確認一點,wstring和wxString 是相容的,可以正確的進行字元轉化。
那麼我們可以通過把string轉化成wstring 然後傳遞給wxString,來實現wxString的初始化。
該方法解決了wxString初始化的問題,但是我們怎麼從wxString中取出值。
有了wstring到wxString的成功案例,我們很容易就聯想到了ToStdWstring方法,官方文件中也是說不存在資料丟失問題,但是很遺憾,經過測試,這個方法還是無效。
在翻閱了wxWidgets支援Unicode 的官方說明文件中 我找到了ToUTF8 這個方法,
( | ) | const |
( | ) | const |
Converts the strings contents to UTF-8 and returns it either as a temporary wxCharBuffer object or as a pointer to the internal string contents in UTF-8 build.
這個方法在 unicode支援 文件中被描述為最可以依賴的無資料丟失的方法,經過驗證,可以使用。
wxString轉string的途徑 : string(wxString.ToUTF8().data())
最後補充上 utf8 string 轉化成 utf16的wstring的方法
/*
源自stackoverflow
*/
std::wstring Convert_Util::utf8_to_utf16(const std::string& utf8)
{
std::vector<unsigned long> unicode;
size_t i = 0;
while (i < utf8.size())
{
unsigned long uni;
size_t todo;
bool error = false;
unsigned char ch = utf8[i++];
if (ch <= 0x7F)
{
uni = ch;
todo = 0;
}
else if (ch <= 0xBF)
{
throw std::logic_error("not a UTF-8 string");
}
else if (ch <= 0xDF)
{
uni = ch&0x1F;
todo = 1;
}
else if (ch <= 0xEF)
{
uni = ch&0x0F;
todo = 2;
}
else if (ch <= 0xF7)
{
uni = ch&0x07;
todo = 3;
}
else
{
throw std::logic_error("not a UTF-8 string");
}
for (size_t j = 0; j < todo; ++j)
{
if (i == utf8.size())
throw std::logic_error("not a UTF-8 string");
unsigned char ch = utf8[i++];
if (ch < 0x80 || ch > 0xBF)
throw std::logic_error("not a UTF-8 string");
uni <<= 6;
uni += ch & 0x3F;
}
if (uni >= 0xD800 && uni <= 0xDFFF)
throw std::logic_error("not a UTF-8 string");
if (uni > 0x10FFFF)
throw std::logic_error("not a UTF-8 string");
unicode.push_back(uni);
}
std::wstring utf16;
for (size_t i = 0; i < unicode.size(); ++i)
{
unsigned long uni = unicode[i];
if (uni <= 0xFFFF)
{
utf16 += (wchar_t)uni;
}
else
{
uni -= 0x10000;
utf16 += (wchar_t)((uni >> 10) + 0xD800);
utf16 += (wchar_t)((uni & 0x3FF) + 0xDC00);
}
}
return utf16;
}