C 字串完全指南 Win32字元編碼(一)

2021-04-13 22:17:26 字數 3896 閱讀 5111

c++字串完全指南 - win32字元編碼(一)

2002-11-14 17:38:53

前言

字串的表現形式各異,象tchar,std::string,bstr等等,有時還會見到怪怪的用_tcs起頭的巨集。這個指南的目的就是說明各種字串型別及其用途,並說明如何在必要時進行型別的相互轉換。

在指南的第一部分,介紹三種字元編碼格式。理解編碼的工作原理是致為重要的。即使你已經知道字串是乙個字元的陣列這樣的概念,也請閱讀本文,它會讓你明白各種字串類之間的關係。

指南的第二部分,將闡述各個字串類,什麼時候使用哪種字串類,及其相互轉換。

字串基礎 - ascii, dbcs, unicode

所有的字串類都起源於c語言的字串,而c語言字串則是字元的陣列。首先了解一下字元型別。有三種編碼方式和三種字元型別。

第一種編碼方式是單位元組字符集,稱之為sbcs,它的所有字元都只有乙個位元組的長度。ascii碼就是sbcs。sbcs字串由乙個零位元組結尾。

第二種編碼方式是多位元組字符集,稱之為mbcs,它包含的字元中有單位元組長的字元,也有多位元組長的字元。windows用到的mbcs只有二種字元型別,單位元組字元和雙位元組字元。因此windows中用得最多的字元是雙位元組字符集,即dbcs,通常用它來代替mbcs。

在dbcs編碼中,用一些保留值來指明該字元屬於雙位元組字元。例如,shift-jis(通用日語)編碼中,值0x81-0x9f 和 0xe0-0xfc 的意思是:「這是乙個雙位元組字元,下乙個位元組是這個字元的一部分」。這樣的值通常稱為前導位元組(lead byte),總是大於0x7f。前導位元組後面是跟隨位元組(trail byte)。dbcs的跟隨位元組可以是任何非零值。與sbcs一樣,dbcs字串也由乙個零位元組結尾。

第三種編碼方式是unicode。unicode編碼標準中的所有字元都是雙位元組長。有時也將unicode稱為寬字符集(wide characters),因為它的字元比單位元組字元更寬(使用更多記憶體)。注意,unicode不是mbcs - 區別在於mbcs編碼中的字元長度是不同的。unicode字串用二個零位元組字元結尾(乙個寬字元的零值編碼)。

單位元組字符集是拉丁字母,重音文字,用ascii標準定義,用於dos作業系統。雙位元組字符集用於東亞和中東語言。unicode用於com和windows nt內部。

讀者都很熟悉單位元組字符集,它的資料型別是char。雙位元組字符集也使用char資料型別(雙位元組字符集中的許多古怪處之一)。unicode字符集用wchar_t資料型別。unicode字串用l字首起頭,如:

wchar_t  wch = l'1';      // 2 個位元組, 0x0031

wchar_t* wsz = l"hello";  // 12 個位元組, 6 個寬字元

字串的儲存

單位元組字串順序存放各個字元,並用零位元組表示字串結尾。例如,字串"bob"的儲存格式為:

unicode編碼中,l"bob"的儲存格式為:

用0x0000 (unicode的零編碼)結束字串。

dbcs 看上去有點象sbcs。以後我們會看到在串處理和指標使用上是有微妙差別的。字串"日本語" (nihongo) 的儲存格式如下(用lb和tb分別表示前導位元組和跟隨位元組):

注意,"ni"的值不是word值0xfa93。值93和fa順序組合編碼為字元"ni"。(在高位優先cpu中,存放順序正如上所述)。

字串處理函式

c語言字串處理函式,如strcpy(), sprintf(), atol()等只能用於單位元組字串。在標準庫中有只用於unicode字串的函式,如wcscpy(), swprintf(), _wtol()。

微 軟在c執行庫(crt)中加入了對dbcs字串的支援。對應於str***()函式,dbcs使用_mbs***()函式。在處理dbcs字串(如日 語,中文,或其它dbcs)時,就要用_mbs***()函式。這些函式也能用於處理sbcs字串(因為dbcs字串可能就只含有單位元組字元)。

現在用乙個示例來說明字串處理函式的不同。如有unicode字串l"bob":

x86 cpu的排列順序是低位優先(little-endian)的,值0x0042的儲存順序為42 00。這時如用strlen()函式求字串的長度就發生問題。函式找到第乙個位元組42,然後是00,意味著字串結尾,於是返回1。反之,用 wcslen()函式求"bob"的長度更糟糕。wcslen()首先找到0x6f42,然後是0x0062,以後就在記憶體緩衝內不斷地尋找00 00直至發生一般性保護錯(gpf)。

str***()及其對應的_mbs***()究竟是如何運作的?二者之間的不同是非常重要的,直接影響到正確遍歷dbcs字串的方法。下面先介紹字串遍歷,然後再回來討論str***()和 _mbs***()。

字串遍歷

我們中的大多數人都是從sbcs成長過來的,都習慣於用指標的 ++ 和 -- 操作符來遍歷字串,有時也使用陣列來處理字串中的字元。這二種方法對於sbcs 和 unicode 字串的操作都是正確無誤的,因為二者的字元都是等長的,編譯器能夠的正確返回我們尋求的字元位置。

但對於dbcs字串就不能這樣了。用指標訪問dbcs字串有二個原則,打破這二個原則就會造成錯誤。

1. 不可使用 ++ 運算元,除非每次都檢查是否為前導位元組。

2. 絕不可使用 -- 運算元來向後遍歷。

假設用以下**來配製檔名:

bool getconfigfilename ( char* pszname, size_t nbuffsize )
}
這段**的保護性是很強的,但用到dbcs字串還是會出錯。假如檔案的安裝路徑用日語表達:c:ヨウユソ,該字串的記憶體表達為:

這時用上面的getconfigfilename()函式來檢查檔案路徑末尾是否含有反斜線就會出錯,得到錯誤的檔名。

錯在**?注意上面的二個十六進製制值0x5c(藍色)。前面的0x5c是字元"",後面則是字元值83 5c,代表字元"ソ"。可是函式把它誤認為反斜線了。

正確的方法是用dbcs函式將指標指向恰當的字元位置,如下所示:

bool fixedgetconfigfilename ( char* pszname, size_t nbuffsize )
}
這個改進的函式用charprev() api 函式將指標plastchar向後移動乙個字元。如果字串末尾的字元是雙位元組字元,就向後移動2個位元組。這時返回的結果是正確的,因為不會將字元誤判為反斜線。

現在可以想像到第一原則了。例如,要遍歷字串尋找字元":",如果不使用charnext()函式而使用++運算元,當跟隨位元組值恰好也是":"時就會出錯。

2a. 絕不可在字串陣列中使用遞減下標。

出錯原因與原則2相同。例如,設定指標plastchar為:

char* plastchar = &szconfigfilename [strlen(szconfigfilename) - 1];
結果與原則2的出錯一樣。下標減1就是指標向後移動乙個位元組,不符原則2。

再談str***()_mbs***()

現在可以清楚為什麼要用 _mbs***() 函式了。str***() 函式不認識dbcs字元而 _mbs***()認識。如果呼叫strrchr("c:", '')函式可能會出錯,但 _mbsrchr()認識雙位元組字元,所以能返回指向最後出現反斜線字元的指標位置。

最後提一下str***() 和 _mbs***() 函式族中的字串長度測量函式,它們都返回字串的位元組數。如果字串含有3個雙位元組字元,_mbslen()將返回6。而unicode的函式返回的是wchar_ts的數量,如wcslen(l"bob") 返回3(本文開頭示例的出錯原因 - 譯註)。

C 字串完全指南 Win32字元編碼(一)

c 字串完全指南 win32字元編碼 一 前言 字串的表現形式各異,象tchar,std string,bstr等等,有時還會見到怪怪的用 tcs起頭的巨集。這個指南的目的就是說明各種字串型別及其用途,並說明如何在必要時進行型別的相互轉換。在指南的第一部分,介紹三種字元編碼格式。理解編碼的工作原理是...

C 字串完全指南 Win32字元編碼(一)

c 字串完全指南 win32字元編碼 一 翻譯 連波 14 11 2002 url 前言 字串的表現形式各異,象tchar,std string,bstr等等,有時還會見到怪怪的用 tcs起頭的巨集。這個指南的目的就是說明各種字串型別及其用途,並說明如何在必要時進行型別的相互轉換。在指南的第一部分,...

C 字串型別說明 Win32 字元編碼

引言 毫無疑問,我們都看到過像 tchar,std string,bstr 等各種各樣的字串型別,還有那些以 tcs 開頭的奇怪的巨集。你也許正在盯著顯示器發愁。本指引將總結引進各種字元型別的目的,展示一些簡單的用法,並告訴您在必要時,如何實現各種字串型別之間的轉換。在第一部分,我們將介紹3種字元編...