補充 C 字串完全指引

2021-03-31 08:56:58 字數 4608 閱讀 3932

寫了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...