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