字符与字符串操作——Windows via C/C++
在最新版的Windows, Windows Vista,它應(yīng)該支持Unicode 5.0。在編程中對(duì)字符與字符串的操作是很普通的,為新的系統(tǒng)寫代碼,盡可能使用Unicode,它提供了更好的性能,以及可以進(jìn)行區(qū)域化。而且與COM及.Net框架互操作時(shí)也有幫助。
緩沖區(qū)溢出是系統(tǒng)漏洞的重要來源,Microsoft對(duì)此提供的c-runtime中包含了一些新函數(shù)用于操作字符串。你應(yīng)該都使用這些新函數(shù)。
在Windows Vista中,每個(gè)字符都用UTF-16(UTF是Unicode Transformation Format)進(jìn)行編碼,它是雙字節(jié)的,這里我們所指的Unicode都指UTF-16編碼。但是雙字節(jié)還不能表示某些語言中的所有字符,對(duì)于這些語言,它支持代理(surrogates),后者是用32位代表一個(gè)字符。
還有另外一些UTF編碼,常見的有:
UTF-8:它對(duì)一些字符用1byte,一些字符用2byte,一些字符用3或4byte表示。這種編碼目前很流行。
UTF-32:它對(duì)每個(gè)字符都用4byte表示,這種表示法效率低,很少使用。
一. 數(shù)據(jù)類型
對(duì)于單字節(jié)與雙字節(jié)字符,它定義分別如下:
char c='a';
char szBuffer[100] = "A string";
wchar_t c=L'a';
wchar_t szBuffer[100] = L"A string";
實(shí)際上,wchar_t是unsigned short類型,另外Microsoft還定義了CHAR, PCHAR, TEXT等類型和宏,這些都是條件定義的,它根據(jù)是否使用Unicode而決定合適的版本。這些定義可以在WinNT.h文件找到。
二. Unicode和ANSI函數(shù)
從Windows NT開始,所有的核心API函數(shù)都使用Unicode版本,所以都要求Unicode字符串。如果你傳遞的是ANSI字符串,則它要進(jìn)行轉(zhuǎn)換,它會(huì)消耗性能的。在之前的Windows版本,很多函數(shù)都提供兩個(gè)版本:XXXW和WWWA,如CreateWindowExW和CreateWindowExA。到Vista,后者只是起一個(gè)轉(zhuǎn)換層的作用。如果你需要建立DLL讓別人使用,也可以考慮
同樣,C-runtime也提供了Unicode版本的相應(yīng)字符串函數(shù),如strlen對(duì)應(yīng)的wcslen(wc表示wide character)。但是盡管這樣,c-runtime函數(shù)對(duì)于字符串操作是非常不安全的,如下:
WCHAR? szBuffer[3] = L"";
wcscpy(szBuffer, L"abc");
上面代碼把四個(gè)字符拷貝到只有三個(gè)字符長的緩沖區(qū)中!這些代碼是緩沖區(qū)溢出的最大根源。對(duì)此,Microsoft提供了安全字符串函數(shù),它在strsafe.h(在Windows SDK中,而不在VC的目錄中)中聲明(這個(gè)頭文件應(yīng)該在最后包括,因?yàn)樗褂昧似渌^文件)
PTSTR _tcscpy(PTSTR strDes, PCTSTR strSrc); //
errno_t _tcscpy_s(PTSTR strDes, size_t numChars, PCTSTR strSrc);
除了這些函數(shù)外,C運(yùn)行時(shí)還提供了對(duì)字符串操作有更多控制的函數(shù)。如:
HRESULT StringCchCat(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc);
HRESULT StringCchCatEx(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc,
?? PTSTR *ppszDestEnd, size_t *pcchRemaining, DWORD dwFlags);
HRESULT StringCchCopy(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc);
HRESULT StringCchCopyEx(PTSTR pszDest, size_t cchDest, PCTSTR pszSrc,
?? PTSTR *ppszDestEnd, size_t *pcchRemaining, DWORD dwFlags);
HRESULT StringCchPrintf(PTSTR pszDest, size_t cchDest,
?? PCTSTR pszFormat, ...);
HRESULT StringCchPrintfEx(PTSTR pszDest, size_t cchDest,
?? PTSTR *ppszDestEnd, size_t *pcchRemaining, DWORD dwFlags,
?? PCTSTR pszFormat,...);
在這些參數(shù)中,Cch表示Count of Characters。另外還有一個(gè)以Cb作為函數(shù)名的字符串操作函數(shù)集,這里Cb指Count of byte,如StringCbCat(Ex), StringCbCopy(Ex)。
在這些函數(shù)中,如果緩沖區(qū)太小,它不會(huì)完成功能并返回STRSAFE_E_INSUFFICIENT_BUFFER。在擴(kuò)展版本的函數(shù)中(Ex),額外的參數(shù)的意思如下:
size_t* pcchRemaining: 目標(biāo)緩沖區(qū)中未用字符的數(shù)目('\0'不計(jì)入),例如把一個(gè)字符拷貝到10長度的緩沖區(qū)中,就得到9。
LPTSTR* ppszDestEnd: 如果它非空,指向目標(biāo)緩沖區(qū)的'\0'字符。
DWORD dwFlags: 一些枚舉值組合。見SDK文檔。
三. Windows字符串函數(shù)
Windows也提供了許多字符串操作函數(shù),一些函數(shù)如lstrcat, lstrcpy等已經(jīng)過時(shí)了,因?yàn)樗鼈儧]有提供緩沖區(qū)溢出檢測(cè)。另外ShlwApi.h還定義了許多格式化操作的字符串函數(shù),特別是數(shù)字值,如StrFormatKBSize和StrFormatByteSize等。見MSDN。
比較字符中的操作很常見,這個(gè)任務(wù)最好調(diào)用CompareString(Ex)和CompareStringOrdinal:
int CompareString(
?? LCID locale,
?? DWORD dwCmdFlags,
?? PCTSTR pString1,
?? int cch1,
?? PCTSTR pString2, int cch2);
它還區(qū)域信息作為比較依據(jù),一般會(huì)取得正確的比較結(jié)果。這里L(fēng)CID一般通過GetThreadLocale函數(shù)取得當(dāng)前線程的區(qū)域ID。當(dāng)然這個(gè)函數(shù)效率上會(huì)慢,對(duì)于比較程序性字符串,如參數(shù),注冊(cè)表鍵,XML元素屬性等,可以使用CompareStringOrdinal,這不考慮區(qū)域信息。
要注意這兩個(gè)函數(shù)返回值與C run-time的相應(yīng)函數(shù)不同,它是1(CSTR_LESS_THAN), 2(CSTR_EQUAL)和3(CSTR_GREATER_THAN),你可以減2獲得與c run-time相應(yīng)的返回值。
四. 編程建議
1. 把字符串作為字符數(shù)組,而不是char數(shù)組或byte數(shù)組
2. 使用通用類型(TCHAR/PTSTR)表示字符和字符串
3. 對(duì)于字節(jié),字節(jié)指針和數(shù)據(jù)緩沖區(qū)使用直接類型BYTE和PBYTE
4. 使用字符串和字符常量使用TEXT或_T宏
5. 進(jìn)行全局替換,如用PTSTR替換PSTR
6. 修改字符串相關(guān)的數(shù)字運(yùn)算問題,如用_countof(szBuffer)而不是sizeof(szBuffer)取得緩沖區(qū)的字符數(shù)。分配內(nèi)存也用malloc(nch*sizeof(TCHAR))而不用malloc(nch),當(dāng)然可以定義如下的宏:
#define chmalloc(nCharacters) (TCHAR*)malloc(nCharacters * sizeof(TCHAR))
7. 避免printf家族函數(shù),特別是%s和%S類型的參數(shù),具體見轉(zhuǎn)換部分
8. 要指定UNICODE符號(hào)
下面是字符串操作函數(shù)的選擇建議
1. 使用安全字符串版本(帶_s后綴或StringCch前綴的)。不使用任何沒有帶目標(biāo)緩沖區(qū)大小的緩沖區(qū)操作函數(shù)。c run-time提供了memcpy_s, memmove_s等函數(shù)使用
2. 利用/GS和/RTCs編譯器選取項(xiàng)來自動(dòng)檢測(cè)緩沖區(qū)溢出問題
3. 不使用Kernel32中的字符串操作函數(shù),如lstrcat, lstrcpy
五. 在Unicode和ANSI間轉(zhuǎn)換
Windows函數(shù)MultiByteToWideChar提供了多字節(jié)字符(非Unicode的多字節(jié)字符)與Unicode字符的轉(zhuǎn)換。
int MultiByteToWideChar(
?? UINT uCodePage,
?? DWORD dwFlags,
?? PCSTR pMultiByteStr,
?? int cbMultiByte,
?? PWSTR pWideCharStr,
?? int cchWideChar);
int WideCharToMultiByte(
?? UINT uCodePage,
?? DWORD dwFlags,
?? PCWSTR pWideCharStr,
?? int cchWideChar,
?? PSTR pMultiByteStr,
?? int cbMultiByte,
?? PCSTR pDefaultChar,
?? PBOOL pfUsedDefaultChar);
函數(shù)的使用過程一般如下:
1. 參數(shù)pWideCharStr傳遞NULL,cchWideChar參數(shù)傳遞0,cbMultiByte傳遞-1,這樣調(diào)用就取轉(zhuǎn)換后的字符數(shù)
2. 根據(jù)1返回的字符數(shù)(cc)分配內(nèi)存塊(cc*sizeof(wchar_t))
3. 再調(diào)用該函數(shù),傳入相應(yīng)參數(shù)進(jìn)行字符串轉(zhuǎn)換
4. 釋放2中產(chǎn)生的內(nèi)存塊
如果你需要編寫兩種類型的字符串都能操作的DLL函數(shù),你也可以Microsoft一樣提供XXXW和XXXA版本的函數(shù),其中后者的實(shí)現(xiàn)在經(jīng)過上面兩個(gè)函數(shù)轉(zhuǎn)換后傳遞給前一個(gè)函數(shù)的實(shí)現(xiàn)。如果要決定某個(gè)文件的字符是否是Unicode字符,可用IsTextUnicode函數(shù):
BOOL IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);
它聲明于AdvApi32.dll中,但是這個(gè)函數(shù)是基于統(tǒng)計(jì)的,這樣可能會(huì)返回一個(gè)不正確的結(jié)果。
轉(zhuǎn)載于:https://www.cnblogs.com/Adon/archive/2009/10/11/Windows.html
總結(jié)
以上是生活随笔為你收集整理的字符与字符串操作——Windows via C/C++的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity Pixel 人物设计(1)
- 下一篇: Ubuntu系统---C++之Eclip