字符格式基礎(chǔ)
首先說(shuō)說(shuō)字符編碼的兩種格式。
一種是將文本字符串編碼成一組以0結(jié)尾的單字節(jié)字符,即ansi字符,每個(gè)字符占8位,即一個(gè)字節(jié)。這種編碼方式的缺陷是,對(duì)于字符本地化的問(wèn)題不能很好的解決,因?yàn)?位空間最多容納256個(gè)字符,不能完全唯一的表示世界上所有的字符。
另一種是Unicode字符,每個(gè)字符都使用UTF-16編碼(一般來(lái)說(shuō),除非特別聲明不使用UTF-16編碼),即一個(gè)字符占16位空間(2字節(jié))。基于這種情況,世界上大多數(shù)語(yǔ)言的字符都可以用一個(gè)唯一的2字節(jié)空間表示。
傳統(tǒng)的ansi字符在C語(yǔ)言中用char數(shù)據(jù)類型表示(1字節(jié))。在源代碼中聲明一個(gè)字符串,則編譯器會(huì)轉(zhuǎn)換為由8位char數(shù)據(jù)類型構(gòu)成的一個(gè)數(shù)組(以"/0"結(jié)尾)
char c='a';//一個(gè)8位的字符
char szbuffer[100]="a string";//含有99個(gè)8位字符和1個(gè)8位結(jié)束符(/0)的數(shù)組
微軟的c/c++編譯器為unicode定義了一個(gè)內(nèi)建的數(shù)據(jù)結(jié)構(gòu),wchar_t,它表示一個(gè)16位的unicode(UTF-16)字符。
wchar_t c=L'a';//一個(gè)16位的字符
wchar_t szbuffer[100]=L"a string";//含有99個(gè)16位字符和1個(gè)16位結(jié)束符(/0)的數(shù)組
此外,windows為了和c語(yǔ)言加以區(qū)別,則定義了自己的數(shù)據(jù)類型。
//WinNT.h
typedef char CHAR;//一個(gè)8位的字符
typedef wchar_t WCHAR;//一個(gè)16位的字符
typedef CHAR *PCHAR;//指向8位字符的指針
typedef CHAR *PSTR;//指向8位字符的指針
typedef CONST CHAR *PCSTR;//指向8位字符的常量指針
typedef WCHAR *PWCHAR;//指向16位字符的指針
typedef WCHAR *PWSTR;//指向16位字符的指針
typedef CONST WCHAR *PCWSTR;//指向16位字符的常量指針
在實(shí)際使用中,使用哪種類型并不重要,但最好保持一致,這樣有利于維護(hù)。另外如果是windows編程,最好使用windows定義的數(shù)據(jù)類型,這樣可與MSDN保持一致,有利于開(kāi)發(fā)。
windows還定義了如下宏和通用數(shù)據(jù)類型,來(lái)方便開(kāi)發(fā)人員。
#ifdef UNICODE
typedef WCHAR TCHAR ,*PTCHAR,PTSTR;
typedef CONST WCHAR *PCTSTR;
#define __TEXT(quote) quote
#define __TEXT(quote) L##quote
#else
typedef CHAR TCHAR ,*PTCHAR,PTSTR;
typedef CONST CHAR *PCTSTR;
#define __TEXT(quote) quote
#endif
#define __TEXT(quote) L##quote
其中黑體部分的類型都是通用數(shù)據(jù)類型,即無(wú)論使用ansi還是unicode,都能通過(guò)編譯。
TCHAR c=TEXT('A');//如果定義了unicode,則a是16位字符;如果定義了ansi,則a是8位字符
TCHAR SZBUFFER[100]=TEXT("A STRING");//如果定義了unicode,則SZBUFFER中的字符為16位;否則,字符位8位。
自windowsNT開(kāi)始,windows所有版本都使用unicode構(gòu)建。即如果我們的程序使用ansi字符,那么操作系統(tǒng)將會(huì)執(zhí)行轉(zhuǎn)換為unicode,這些轉(zhuǎn)換都是對(duì)用戶透明的,且會(huì)產(chǎn)生時(shí)間和內(nèi)存的開(kāi)銷。所以,我們應(yīng)盡量使用unicode構(gòu)建程序。
至此為止,總結(jié)一下,就是定義字符用TCHAR,指針類型使用*TCHAR足以,定義字面值用_T宏。
windows中的函數(shù)的參數(shù)列表中如果有字符串,則該函數(shù)通常有兩個(gè)版本,一個(gè)ansi,一個(gè)unicode。例如CreateWindowEx接受這兩種類型的字符串作為參數(shù)。實(shí)際上,CreateWindowEx只是一個(gè)宏,定義如下:
//WinUser.h
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif
很明顯,這和前面的通用數(shù)據(jù)類型使用的是同一伎倆。其中CreateWindowExW接受Unicode,CreateWindowExA接受ansi。
在windows vista中,CreateWindowExA只是一個(gè)轉(zhuǎn)換層,負(fù)責(zé)分配內(nèi)存,然后將ansi轉(zhuǎn)換為unicode,然后在內(nèi)部調(diào)用CreateWindowExW。
C運(yùn)行庫(kù)中的安全字符串處理函數(shù)
通常,修改字符串的函數(shù)都存在一個(gè)安全隱患,即目標(biāo)字符串的緩沖不夠大,則會(huì)導(dǎo)致內(nèi)存中的數(shù)據(jù)被破壞。
例如:
WCHAR szbuffer[3]=L"";
wcscpy(szbuffer,L"abc");
以上的例子是以0結(jié)尾的,需要szbuffer[4]才可以容納。但編譯時(shí)并不會(huì)有任何報(bào)錯(cuò)或警告。
針對(duì)以上問(wèn)題,我們必須使用“安全字符串處理函數(shù)”,這一類的函數(shù)以_s結(jié)尾(secure之意),我們來(lái)看一下這類函數(shù)的原型.
PTSTR _tcscpy(PTSTR strDestination,PCTSTR strSource);
errno_t _tcscpy_s(PTSTR strDestination,size_t numberOfCharacters,PCTSTR strSource);
PTSTR _tcscat(PTSTR strDestination,PCTSTR strSource);
errno_t _tcscat_s(PTSTR strDestination,size_t numberOfCharacters,PCTSTR strSource);
可以看到,在將一個(gè)緩沖區(qū)作為目標(biāo)緩沖區(qū)時(shí),必須提供這個(gè)緩沖區(qū)的大小(可容納的字符個(gè)數(shù)),通過(guò)調(diào)用_countof宏計(jì)算出來(lái)。
這個(gè)緩沖區(qū)大小參數(shù)的主要任務(wù)就是驗(yàn)證緩沖區(qū)是否足以容納結(jié)果數(shù)據(jù).
現(xiàn)在,我們調(diào)用這些安全函數(shù)的時(shí)候,就可以檢查返回的errno_t值。只有返回S_OK值,才表明函數(shù)調(diào)用成功。其他的值可以參照errno.h中的定義。
來(lái)看一個(gè)例子:
TCHAR szbuffer[3]=L"";
_tcscpy_s(szbuffer,_countof(szbuffer),_T("abc"));
執(zhí)行完后, szbuffer的第一個(gè)字符被設(shè)置為'/0',而其他所有字節(jié)全部被設(shè)置為0xfd(填充符)。
在處理字符串時(shí)獲得更多的控制
除了安全字符串處理函數(shù),c運(yùn)行庫(kù)還增加了一些函數(shù),用于在處理字符串時(shí)獲得更多的控制。例如,可以控制如何截?cái)嘧址?/p>
這類函數(shù)具有兩個(gè)版本,對(duì)應(yīng)ansi和unicode。
HRESULT StringCchCat( LPTSTR pszDest,size_t cchDest,LPCTSTR pszSrc);
HRESULT StringCchCatEx( LPTSTR pszDest,size_t cchDest, LPCTSTR pszSrc,LPTSTR *ppszDestEnd,size_t *pcchRemaining,DWORD dwFlags);
HRESULT StringCchCopy( LPTSTR pszDest,size_t cchDest, LPCTSTR pszSrc);
HRESULT StringCchCopyEx(LPTSTR pszDest,size_t cchDest, LPCTSTR pszSrc, LPTSTR *ppszDestEnd,size_t *pcchRemaining,DWORD dwFlags);
可以看出,在所有方法的名稱中,都含有一個(gè)“Cch”,這表示count of characters,即字符個(gè)數(shù)。通常使用_countof宏獲得。
另外還有一些函數(shù)含有“cb”,這表示函數(shù)要求用字節(jié)數(shù)來(lái)指定大小。通常使用sizeof()獲得。
這些函數(shù)返回HRESULT,具體的值
S_OK 成功。目標(biāo)緩沖區(qū)中包含源字符串,并以/0終止
STRSAFE_E_INVALID_PARAMETER 失敗。將NULL傳給了一個(gè)參數(shù)
STRSAFE_E_INSUFFICIENT_BUFFER 失敗。指定的目標(biāo)緩沖區(qū)太小,無(wú)法容納整個(gè)源字符串
不同于安全字符串處理函數(shù),這類函數(shù)運(yùn)行時(shí),當(dāng)緩沖區(qū)太小,則會(huì)執(zhí)行截?cái)唷?/p>
windows字符串處理函數(shù)
在shlwapi.h定義了大量好用的字符串函數(shù),可以用來(lái)對(duì)操作系統(tǒng)有關(guān)的數(shù)值進(jìn)行格式化操作。
在實(shí)際編程中,經(jīng)常需要比較兩個(gè)字符串是否相等。對(duì)于這樣的需求可以使用CompareString(Ex)或者CompareStringOrdinal。
CompareString(Ex)一般用于向用戶顯示的字符串。
CompareStringOrdinal一般用于比較程序內(nèi)部的字符串,如路徑名,注冊(cè)表項(xiàng)值等。
CompareString(Ex)的處理速度相對(duì)于CompareStringOrdinal較慢。
這兩個(gè)函數(shù)的返回0,則表示調(diào)用失敗;
返回1(CSTR_LESS_THAN)表明string1小于string2;
返回2(CSTR_GREATER_THAN)表明string1大于string2.
使用unicode的好處:
1、有利于應(yīng)用程序的本地化。
2、代碼執(zhí)行效率更高,因?yàn)閣indows基于unicode構(gòu)建,如果我們也保持一致,則windows處理時(shí)不必耗費(fèi)額外的資源轉(zhuǎn)換。
3、容易與com集成
4、容易與.net集成
推薦的字符處理方式
1、使用通用數(shù)據(jù)類型和宏,如TCHAR和_T
2、將字符串想象成字符的數(shù)組,而非char或字節(jié)的數(shù)組。
3、與字符串有關(guān)的計(jì)算需修改。尤其要搞清楚很多的函數(shù)要求傳遞的是字符數(shù)還是字節(jié)數(shù),如是前者,則用_countof()獲取;如是后者,則用(字符數(shù)*sizeof(字符類型))獲取。
4、避免使用printf系列函數(shù)轉(zhuǎn)換ansi和unicode。正確的做法是使用MultiByteToWideChar或WideCharToMultiByte。
5、始終使用安全的字符串處理函數(shù),_s或stringcch,如要控制截?cái)啵瑒t使用后者。
6、如果一個(gè)緩沖區(qū)處理函數(shù)的參數(shù)中不包括目標(biāo)緩沖區(qū)的長(zhǎng)度,那么避免使用。
7、比較字符串時(shí),使用CompareStringOrdina和CompareString(Ex)。前者速度快,適合處理程序內(nèi)部的字符串;后者用于處理UI的字符串,因?yàn)樗且詤^(qū)域設(shè)置來(lái)適當(dāng)排序,速度較慢。
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/shentao17792/archive/2010/02/21/5314312.aspx
聯(lián)系客服