寫了n年程式,近來在字串上栽了。:( 認真的研究了一些關於字串的文章,在此記下。
許多關於字串的問題,在文章最後的參考文章中,相信有更加深入和精確的描述。不過關於中文的處理,我想先補充一些自己的看法。
背景:win32 console程式,使用printf輸出字串。相信許多人都有使用過。
平台:visualstudio.*** 2003(mfc 7.1)。
mbcs
unicode
蔡b2 cc
21 85
a41 00
41 00
程式段1:使用std::string
#include
以上**,不管使用mbcs還是_unicode編譯,得到的結果都是一樣的。因為string (實際上是basic_string)不會自動進行從mbcs到unicode的轉換。所以使用printf或者wprintf輸出即可。前提當然是你的系統需要支援中文。
讓我們把**修改一下,希望可以輸出字串的內容:
outputdebugstring實際上就是atltrace()最後呼叫的函式,該函式向visualstudio的output視窗輸出,而printf和wprintf向console視窗輸出。最後的結果如何?outputdebugstringw輸出的是怪字元!!why?? s1.c_str()傳遞給outputdebugstringw和wprintf不是都是內容相同的lpcwstr嗎?
1)因為outputdebugstringw的字串必須是真正unicode編碼的字串,而不是所有的const wchar_t*(即lpcwstr)都可以得到正確結果。在這裡s1雖然使用wchar_t型別,但是實際的內容卻是mbcs編碼。
2)反之亦然:crt的wprintf只支援mbcs編碼的字串,而不能是unicode編碼的字串。在程式段2我們可以看到真正的unicode編碼的字串。
這是我多年來的乙個誤區:wchar_t型別的字串就是unicode字串。實際應該理解為unicode是16位的字符集,可以使用wchar_t型別進行儲存。
程式段2:使用cstring
請看程式後面的說明。
1.// start: ***pile with_unicode ///
2.5.
8.11.
14.17.
20.// end: ***pile with _unicode ///
21.// start: ***pile with_mbcs ///
22.25.
28.31.
34.37.
40.// end: ***pile with _mbcs ///
1)對於英文本母『a』,mbcs和unicode的結果都是一樣的
2)line 15.
cstring s1 (l"
蔡"); // b2 00 cc 00 00 00
得到的還是mbcs的字串,只是增加了0作為trail byte。而不是我理解的unicode字串!這是我多年來的另外乙個誤區:_t在_unicode下轉換為l,而l後面的字串是unicode編碼。在參考資料msdn的「
」中(以及msdn的許多地方),可以看到類似的說明:
一般文字資料型別對映
一般文字資料型別名
未定義 _unicode 或 _mbcs
已定義_mbcs
已定義 _unicode
_tchar
char
char
wchar_t
_tint
int
int
wint_t
_tschar
signed char
signed char
wchar_t
_tuchar
unsigned char
unsigned char
wchar_t
_txchar
char
unsigned char
wchar_t
_t或_text
無效(由預處理器移除)
無效(由預處理器移除)
l(將後面的字元或字串轉換成相應的 unicode 形式)
實際上l"***"只是通知編譯器,我們需要的是wchar_t型別的字串,而不能影響編碼。
真正的unicode字串在**?
3)line 12:
等同於cstringw s1 ("
蔡"); // 21 85 00 00
我們看到,得到了真正的unicode 字串。因為cstring(在mfc 7.1中,不存在mfc的cstring,實際上由atl::cstringt通過typedef定義而得)的建構函式,在這裡實際上是cstringw的建構函式,根據輸入的引數是char型別字串,會自動呼叫multibytetowidechar轉換mbcs字串為unicode字串。
4)相應line 12,那麼line 35得到的結果32 a8 ac 00是什麼?和line 12類似:
cstring建構函式,在這裡實際上是cstringa的建構函式,根據輸入的引數是wchar_t型別字串,會自動呼叫widechartomultibyte轉換unicode字串為mbcs字串。但是根據2),我們知道,輸入的引數不是unicode字串,只是mbcs的wchar_t型別字串,所以得到的是錯誤的編碼。
總結以上,可知:
1)crt不能生成和處理unicode型別字串,對於wchar_t型別字串,只能處理mbcs編碼;
2)vc runtime中帶w字尾的函式,和所有的***函式,對於wchar_t型別字串,只能處理unicode編碼;
3)如果不考慮輸出,只是進行拷貝、比較等操作,只要注意_t的含義和字串的字元型別長度就可以了;但是如果需要輸出,必須注意字串的編碼轉換。
4)使用unicode或者mbcs的編譯選項,只是影響字串的字元型別(自動識別_t,cstring等,自動把函式轉換為***xa()或者***xw()),不影響字串的編碼。下表的**結果不受編譯選項影響(這也是編譯器處理_t,cstring等後的結果):
cstringa s = "***"; // 等於 ca2a("***")
結果為sbcs(單位元組編碼)
printf()正確
outputdebugstringa()正確
cstringw s = "***"; // 等於 ca2w("***")
結果為unicode編碼
wprintf()錯誤
outputdebugstringw()正確
cstringa s = l"***"; // 等於 cw2a(l"***")
結果為mbcs編碼(可能錯誤)
printf()錯誤
outputdebugstringa()錯誤
cstringw s = l"***"; // 等於 cw2w("***")
不改變字串的編碼,仍然是mbcs。
wprintf()正確
outputdebugstringw()錯誤
疑問:我認為對於cw2w是由系統編碼決定,可以直接得到unicode嗎?
關於cw2a,如果後面的字串的確是unicode編碼,則可以得到正確的相應mbcs編碼字串。實際上,這也是我們要輸出unicode的方法:
cstringw s = "蔡"; // s 現在是unicode編碼
// wprintf(s)不正確
cw2a psz(s); // psz現在是s相應的正確的mbcs編碼!
printf(psz); // 正確
// all is ok, a little more to say
ca2w wsz(psz); // wsz現在是psz的錯誤的unicode編碼,即32 a8 ac 00
推薦參考資料
c++字串完全指引之一 —— win32 字元編碼
c++字串完全指引之二 —— 字串封裝類
其它一篇
stl 字串類與 unicode
當然,少不了msdn
補充 C 字串完全指引 htm
寫了n年程式,近來在字串上栽了。認真的研究了一些關於字串的文章,在此記下。許多關於字串的問題,在文章最後的參考文章中,相信有更加深入和精確的描述。不過關於中文的處理,我想先補充一些自己的看法。背景 win32 console程式,使用printf輸出字串。相信許多人都有使用過。平台 visualst...
補充 C 字串完全指引 htm
寫了n年程式,近來在字串上栽了。認真的研究了一些關於字串的文章,在此記下。許多關於字串的問題,在文章最後的參考文章中,相信有更加深入和精確的描述。不過關於中文的處理,我想先補充一些自己的看法。背景 win32 console程式,使用printf輸出字串。相信許多人都有使用過。平台 visualst...
C 字串完全指引 htm
寫了n年程式,近來在字串上栽了。認真的研究了一些關於字串的文章,在此記下。許多關於字串的問題,在文章最後的參考文章中,相信有更加深入和精確的描述。不過關於中文的處理,我想先補充一些自己的看法。背景 win32 console程式,使用printf輸出字串。相信許多人都有使用過。平台 visualst...