字元和字串處理(2)
2.5 安全字串函式
不安全的字串函式 |
Strsafe函式 |
Safe CRT函式 (C執行庫) |
strcpy, wcscpy, _tcscpy, _mbscpy, strcpy , lstrcpy, _tccpy, _mbccpy |
StringCchCopy StringCbCopy StringCchCopyEx StringCbCopyEx |
strcpy_s |
strcat, wcscat , _mbscat, strcat, lstrcat, , strcatbuff, strcatchain, _tccat, _mbccat |
StringCchCat StringCbCat StringCchCatEx StringCbCatEx |
strcat_s |
wnsprintf, wsprintf, sprintf, swprintf, _stprintf |
StringCchPrintf StringCbPrintf StringCchPrintfEx StringCbPrintfEx |
_snprintf_s _snwprintf_s |
目標緩衝區太小時,不發生截斷 |
目標緩衝區太小時,會發生截斷 |
不發生截斷 |
★String Safe函式:微軟提供的內聯形式函式,可以當做API(在strsafe.h檔案中,注意要在包含其他檔案之後,才包含該檔案!)
★Safe CRT函式:C執行庫
【SafeString程式】自定義錯誤處理函式
/*--------------------------------------------------------------------------------------- 功能:實現課本P19頁,自定義函式呼叫失敗處理程式 ----------------------------------------------------------------------------------------*/ #include <tchar.h> #include <stdlib.h> #include <windows.h> #include <stdint.h> #include <crtdbg.h> //要用到_CrtSetReportMode函式 #include <strsafe.h> //自定義函式呼叫失敗的處理程式——只有在Debug版下才有效,Release中所有的引數將被傳入NULL或0。 //當某個函式呼叫失敗,系統會呼叫該函式,同時傳入“錯誤描述文字”、出錯的函式名稱,檔名及出錯所在行數。 void InvalidParameterHandle(PCTSTR expression, PCTSTR function, PCTSTR file, unsigned int line, uintptr_t /*pReserved*/) { _tprintf(_T("expression %s,\nfunction %s,\nfile %s,\nline %d\n"),expression,function,file,line); } int _tmain() { _CrtSetReportMode(_CRT_ASSERT, 0); //禁用“除錯斷言失敗”對話方塊 TCHAR szBefore[5] = { _T('B'), _T('B'), _T('B'), _T('B') ,'\0'}; TCHAR szBuffer[10] = { _T('-'), _T('-'), _T('-'), _T('-'), _T('-'), _T('-'), _T('-'), _T('-'), '\0' }; TCHAR szAfter[5] = { _T('A'), _T('A'), _T('A'), _T('A'), '\0' }; //註冊函式呼叫失敗的處理程式 _set_invalid_parameter_handler(InvalidParameterHandle); //源字串10個字元(不含\0),目標緩衝區,只能容納9個,會出錯(發生錯誤時,不彈出Debug //Assertion Failure對話方塊而是呼叫自定義的InvalidParameterHandle函式) errno_t ret = _tcscpy_s(szBuffer, _countof(szBuffer), _T("0123456789")); return 0; }
2.6 Str Safe函式的介紹(須含包strsafe.h檔案)——目標緩衝區太小時,會發生截斷。
(1)StringCchXXXX函式——其中的Cch表示字元的個數,可用_countof(pszDest)計算
①StringCchCopy——複製一個字串到緩衝區,但要求提供目標緩衝區的長度,以確保寫入資料不會超出緩衝區的末尾使用該函式替代以下函式:strcpy、wcscpy、_tcscpy、lstrcpy、StrCpy等函式。
引數 |
描述 |
LPTSTR pszDest |
緩衝區,用於接收拷貝過來的字串 |
size_t cchDest |
①目標緩衝區的大小(字元個數)——_countof(pszDest) ②該值必須大於或等於 lstrlen(pszSrc) + 1(待拷貝字串的字元+'\0') ③這個數不能超過 STRSAFE_MAX_CCH。 |
LPCTSTR pszSrc |
待拷貝的字串 |
返回值 |
①S_OK:字串正常拷貝 ②STRSAFE_E_INVALID_PARAMETER:cchDest 引數的值為 0或cchDest 引數的值大於 STRSAFE_MAX_CCH。 ③STRSAFE_E_INSUFFICIENT_BUFFER:因緩衝區空間不足導致失敗;結果被截斷,但仍然包含'\0'結尾;如果截斷操作可以被接受,則不一定被看作是失敗,即這時的返回值也可認為是可接受的。 |
②StringCchCat——將一個字串拼接到另一個字元,使用該函式替代以下函式:strcat、 wcscat、_tcsat、lstrcat、StrCat和StrCatBuff等函式。
引數 |
描述 |
LPTSTR pszDest |
①目標緩衝區,同時包含第一個字串 ②該緩衝區應該大於或等於(lstrlen(pszDest) +lstrlen(pszSrc) + 1)*sizeof(TCHAR) |
size_t cchDest |
①目標緩衝區的大小(字元個數)——_countof(pszDest) ②該緩衝區必須大於或等於lstrlen(pszDest) +lstrlen(pszSrc) + 1(兩個字串的字元總和+'\0') ③這個數不能超過 STRSAFE_MAX_CCH。 |
LPCTSTR pszSrc |
第2個字串 |
返回值 |
①S_OK:字串正常拼接 ②STRSAFE_E_INVALID_PARAMETER:cchDest 引數的值為 0或cchDest 引數的值大於 STRSAFE_MAX_CCH;目標緩衝區空間已滿。 ③STRSAFE_E_INSUFFICIENT_BUFFER:因緩衝區空間不足導致失敗;結果被截斷,但仍然包含'\0'結尾;如果截斷操作可以被接受,則不一定被看作是失敗。 |
③StringCchLength——用於確定字串是否超過了規定的長度,以字元為計算單位(不含\0),用來替換strlen、wcslen和_tcslen函式。
引數 |
描述 |
LPCTSTR psz |
指向待檢查的字串 |
size_t cchMax |
①psz 引數裡最大允許的字元數量,包括'\0' ②這個數不能超過 STRSAFE_MAX_CCH。 |
size_t *pcch |
①psz 引數指向字串的字元個數,不包括'\0',字串長度返回這引數中。 ②這個值只有在 psz 指標不為 NULL,且函式成功時有效 |
返回值 |
①S_OK:psz 指向的字串不為空,且字串的長度(包括'\0')小於等於 cchMax ②STRSAFE_E_INVALID_PARAMETER:psz 指向空字串;cchMax 的值大於STRSAFE_MAX_CCH;psz 指向的字串的字元個數超過 cchMax。 |
④StringCchPrintf——把資料格式化寫入到指定的緩衝區裡,使用該函式替代以下函式:
Sprintf、swprintf、 _stprintf、wsprintf、wnsprintf、_snprintf、_snwprintf和 _sntprintf等函式
引數 |
描述 |
LPTSTR pszDest |
指定格式化資料將要寫入的緩衝區 |
size_t cchDest |
①緩衝區大小,應該設定足夠大,以容納字串和結束標記('\n') ②最大允許的字元數是 STRSAFE_MAX_CCH |
LPCTSTR pszFormat |
①格式化字串,與 pirntf 的格式化字串一致 ②這個值只有在 psz 指標不為 NULL,且函式成功時有效 |
... |
可變引數,引數的個數取決 pszFormat 引數 |
返回值 |
①S_OK:psz 表示有足夠的空間將拷貝到 pszDest,沒有發生截斷 ②STRSAFE_E_INVALID_PARAMETER:cchDest 的值為 0 或大於 STRSAFE_MAX_CCH。 ③STRSAFE_E_INSUFFICIENT_BUFFER:由於緩衝區空間不足而導致的複製失敗;結果被截斷,當仍然包含'\0'結尾;如果截斷操作可以被接受,則不一定被看作是失敗。 |
(2)StringCcbXXXX函式:其中的Ccb表示目標緩衝區的位元組數,可用sizeof(pszDest)求
(3)StringCchXXXXEx和StringCcbEx等擴充套件版本的函式,(見課本P22頁)
2.7 Unicode與ANSI字串的轉換
(1)MultiByteToWideChar——多位元組字串轉為Unicode字串
引數 |
描述 |
UINT uCodePage |
指定與ANSI字串關聯的內碼表。 CP_ACP:指定為當前系統內碼表 CP_OEMCP 當前系統OEM內碼表 |
DWORD dwFlags |
指定如何處理沒有轉換的字元,一般設為0。 |
PCTSTR pMultiByteStr |
要轉換的字串 |
int cbMultiByte |
要轉換字串的長度(位元組數),當-1時,函式會自動判斷源字串的長度 |
PWSTR pWideCharStr |
轉換後的Unicode字串存入的目標緩衝區 |
int cchWideChar |
目標緩衝區最大的長度(字元數) |
返回值 |
函式失敗,返回0,可呼叫GetLastError函式 函式成功,兩種情況: A、cchWideChar設為0,則返回值為轉換後寬字元數(含\0)。 B、cchWideChar設為非0,返回寫入目標緩衝區的字元數(包括字串結尾的NULL); |
★★轉換的步驟
①計算所需目標緩衝區的大小:-先將目標緩衝區為NULL,長度為0,表示不轉換隻統計。
iSize = MultiByteToWideChar(CP_ACP,0,pMultiByteStr,-1,NULL,0);
②分配iSize*sizeof(wchar_t)大小的目標緩衝區。
③再次呼叫MultiByteToWideChar(CP_ACP,0,pMultiByteStr,-1, pWideCharStr,iSize);
④使用轉換後的字串
⑤釋放Unicode字串記憶體塊
(2)WideCharToMultiByte——寬位元組字串轉為多位元組字串
引數 |
描述 |
UINT uCodePage |
指定新字串相關聯的ANSI內碼表。 CP_ACP:指定為當前系統內碼表 CP_OEMCP 當前系統OEM內碼表 CP_UTF8:將寬位元組轉為UTF_8 |
DWORD dwFlags |
指定如何處理沒有轉換的字元,一般設為0。 |
PCWSTR pWideCharStr |
要轉換的字串 |
int cchWideChar |
要轉換字串的長度(字元數),當-1時,函式會自動判斷源字串的長度 |
PSTR pMultiByteStr |
轉換後的多位元組字串存入的目標緩衝區 |
int cbMultiByte |
目標緩衝區最大的長度(位元組數) |
pDefaultChar |
在uCodePage指定的內碼表中找不到對應的字元時,用該字元來替換,如果設為NULL,則會用系統預設的字元,一般是?替換。 |
pfUsedDefaultChar |
轉換過程中,如果至少有一個被pDefaultChar指定的字元替換,則這裡會被設為TRUE,否則,設為FALSE。可以通過這個變數,驗證轉換是否成功。該引數一般設為NULL。 |
返回值 |
函式失敗,返回0,可呼叫GetLastError函式 函式成功,兩種情況: A、cbMultiByte設為0,則返回值為轉換後所需的位元組數(含\0)。 B、cbMultiByte設為非0,返回寫入目標緩衝區的位元組數(包括字串結尾的NULL); |
★★轉換的步驟——與MultiByteToWideChar相似,唯一不同的是第1 次呼叫時返回值就是本身所需的位元組數,所以無需進行乘法運算。
(3)利用ATL庫中的函式來轉換字符集
①使用時先#include <atlconv.h>,然後呼叫巨集USES_CONVERSION(只需呼叫一次)
②巨集A2T、A2W、W2T、W2A進行轉換
★2表示to,W表示Unicode,A表示ANSI(或MBCS),T表示通用型別(看UNICODE巨集)
★使用 ATL 轉換巨集,在棧上分配記憶體,由於不用釋放臨時空間,所以使用起來非常方便。但使用時要注意幾點:
A:只適合於進行短字串的轉換;
B:不要試圖在一個次數比較多的迴圈體內進行轉換;
C:不要試圖對字元型檔案內容進行轉換,因為檔案尺寸一般情況下是比較大的;
D:對情況 B 和 C,要使用 MultiByteToWideChar和 WideCharToMultiByte;
【AnsiAndUnicode】多位元組與Unicode之間的轉換
/*------------------------------------------------------------------------------------
多位元組與Unicode之間的轉換
-------------------------------------------------------------------------------------*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <locale.h>
#include <atlconv.h> //ATL庫提供了更簡便的字符集轉換函式
//Ansi轉化Unicode
TCHAR* AnsiToUnicode(PCSTR pMuliByteStr)
{
int iCount;
TCHAR* pRet;
//計算所需緩衝區大小(字元數)
iCount = MultiByteToWideChar(CP_ACP, 0, pMuliByteStr,-1,NULL,0);//返回字元個數(含\0)
pRet = (TCHAR*)malloc(iCount*sizeof(wchar_t)); //分配目標緩衝區大小
MultiByteToWideChar(CP_ACP, 0, pMuliByteStr, -1, pRet, iCount);
return pRet;
}
//Unicode轉化Ansi
char* UnicodeToAnsi(wchar_t* pWideChar)
{
int iCount;
char* pRet;
//計算所需緩衝區大小(位元組數)
iCount = WideCharToMultiByte(CP_ACP, 0, pWideChar, -1, NULL, 0,NULL,NULL);//返回位元組數(含\0)
pRet = (char*)malloc(iCount); //分配目標緩衝區大小
WideCharToMultiByte(CP_ACP, 0,pWideChar,-1,pRet,iCount,NULL,NULL);
return pRet;
}
//UnicodeToUTF8
char* UnicodeToUTF8(wchar_t* pWideChar)
{
int iCount;
char* pRet;
//計算所需緩衝區大小(位元組數)
iCount = WideCharToMultiByte(CP_UTF8, 0, pWideChar, -1, NULL, 0, NULL, NULL);//返回位元組數(含\0)
pRet = (char*)malloc(iCount); //分配目標緩衝區大小
WideCharToMultiByte(CP_UTF8, 0, pWideChar, -1, pRet, iCount, NULL, NULL);
return pRet;
}
//UTF8轉化Unicode
TCHAR* UTF8ToUnicode(PCSTR pMuliByteStr)
{
int iCount;
TCHAR* pRet;
//計算所需緩衝區大小(字元數)
iCount = MultiByteToWideChar(CP_UTF8, 0, pMuliByteStr, -1, NULL, 0);//返回字元個數(含\0)
pRet = (TCHAR*)malloc(iCount*sizeof(wchar_t)); //分配目標緩衝區大小
MultiByteToWideChar(CP_UTF8, 0, pMuliByteStr, -1, pRet, iCount);
return pRet;
}
int _tmain()
{
setlocale(LC_ALL, "chs");
//Ansi字元
char pStringA[] = "123ABC漢字абвгде╔╗╚╝╠╣╦";
printf("Ansi轉化為Unicode\n");
printf("轉換前pStringA:%s\t長度:%d(bytes)\n", pStringA,sizeof(pStringA));
wchar_t* pStringB = AnsiToUnicode(pStringA);
wprintf(L"轉換後pStringB:%s\t長度:%d(bytes)\n", pStringB,(lstrlenW(pStringB)+1)*sizeof(wchar_t));
printf("\n將Unicode(pStringB)再轉為Ansi\n");
char* pStringC = UnicodeToAnsi(pStringB);
printf("轉換後pStringC:%s\t長度:%d(bytes)\n", pStringC, (strlen(pStringC) + 1));
printf("\nUnicode(pStringB)轉為UTF-8\n");
char* pStringD = UnicodeToUTF8(pStringB);
wprintf(L"轉換後pStringD:%s\t長度:%d(bytes)\n", pStringD,(strlen(pStringD) + 1));
printf("將UTF-8(pStringD)轉換後的UTF-16\n");
wchar_t* pStringE = UTF8ToUnicode(pStringD);
wprintf(L"轉換後pStringE:%s\t長度:%d(bytes)\n", pStringE, (lstrlenW(pStringE) + 1)*sizeof(wchar_t));
USES_CONVERSION; //要使用ATL庫,需呼叫該巨集,只須呼叫一次。
//使用ATL庫進行字符集轉換
printf("\n利用ATL庫的A2W將Ansi轉化為Unicode\n");
wchar_t* pTmp = A2W(pStringA);
wprintf(L"轉換前pStringA:%s\t長度:%d(bytes)\n", pTmp, (lstrlenW(pTmp) + 1)*sizeof(wchar_t));
printf("\n利用ATL庫的W2A將Unicode轉化為ANSI\n");
char* pTmpChar = W2A(pTmp);
printf("轉換前pTmp :%s\t長度:%d(bytes)\n", pTmpChar, (strlen(pTmpChar) + 1));
free(pStringB);
free(pStringC);
free(pStringD);
free(pStringE);
return 0;
}
2.8 其他與字串有關的函式
(1)IsTextUnicode——判斷檔案是否包含Unicode或ANSI(利用統計規律,並不精確)
引數 |
描述 |
LPVOID lpBuffer |
要測試的字串,其緩衝區地址 |
int cb |
lpBuffer指向的位元組數(注意是不是字元數) |
LPINT lpi |
是個in/out型別的,傳入時指定哪些測試專案,傳出時為符合哪個測試專案。如果為NULL,表示執行每一項測試。 |
返回值 |
TRUE或FALSE |
(2)系統本地語言支援(National Language Support)
①GetACP函式:可以檢索系統中ANSI內碼表識別符號。
②GetCpInfo函式:可以得到內碼表中的詳細資訊:最大字元大小、預設字元、前置符(詳見CPINFO結構體)
③IsDBCSLeadByte函式:可以判定所給字元是否是雙位元組字元的第一個位元組。
④獲取系統安裝的語言版本,系統區域設定等資訊
函式 |
描述 |
GetSystemDefaultLCID |
獲取系統預設的區域設定ID;具體可以對照一下:控制面板è區域和語言選項→高階; |
GetUserDefaultLCID |
獲取當前使用者預設的區域設定ID; 具體可以對照一下:控制面板è區域和語言選項→區域選項 |
GetLocaleInfo |
根據區域設定ID來獲取本地語言名稱 |
GetSystemDefaultLangID |
取得系統預設ID 對應的國家地區 |
GetUserDefaultLangID |
為當前使用者取得預設語言ID |
【StringReverse程式】反轉字串(動態連結庫中提供ANSI和Unicode兩種版本的匯出函式)
/*---------------------------------------------------------------------------------------
匯出ANSI和Unicode 動態連結庫函式
---------------------------------------------------------------------------------------*/
#include <windows.h>
#include "..\ReverseDLL\ReserveDll.h"
#include <tchar.h>
#include <stdio.h>
//靜態呼叫動態庫
#pragma comment(lib,"..\\Debug\\reverseDll.lib")
int _tmain()
{
//Unicode版的翻轉
printf("Unicode版的翻轉\n");
wchar_t szWideChar[] = L"I am a Student.";
printf("翻轉前字串:%ws\n", szWideChar);
StringReverseW(szWideChar, lstrlenW(szWideChar));
printf("翻轉後字串:%ws\n", szWideChar);
printf("\nANSI版的翻轉\n");
char szAnsiBuffer[] = "You are a teacher!";
printf("翻轉前字串:%s\n", szAnsiBuffer);
StringReserseA(szAnsiBuffer, strlen(szAnsiBuffer));
printf("翻轉後字串:%s\n", szAnsiBuffer);
int a = sizeof("A");
return 0;
}
#pragma once
#include <windows.h>
#ifdef _cplusplus
#ifdef API_EXPORT
#define EXPORT extern "C" __declspec(dllexport) //當頭檔案供動態庫本身使用時
#else
#define EXPORT extern "C" __declspec(dllimport) //當頭檔案供呼叫庫的程式使用時
#endif
#else
#ifdef API_EXPORT
#define EXPORT __declspec(dllexport) //當頭檔案供動態庫本身使用時
#else
#define EXPORT __declspec(dllimport) //當頭檔案供呼叫庫的程式使用時
#endif
#endif
EXPORT BOOL StringReverseW(PWSTR pWideCharStr, DWORD cchLength);
EXPORT BOOL StringReserseA(PSTR pMultiByteStr, DWORD cchLength);
#ifdef UNICODE
#define StringReverse StringReverseW
#else
#define StringReverse StringReserseA
#endif // DEBUG
#define API_EXPORT
#include "ReserveDll.h"
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
return TRUE;
}
EXPORT BOOL StringReverseW(PWSTR pWideCharStr, DWORD cchLength)
{
//獲取字串最後一個字元的指標
PWSTR pEndOfStr = pWideCharStr + wcsnlen_s(pWideCharStr, cchLength) - 1;
wchar_t cCharT; //臨時變數
//迴圈直到到達字串中間的字元
while (pWideCharStr <pEndOfStr)
{
//交換字元
cCharT = *pWideCharStr; //將變數儲存在臨時變數中
*pWideCharStr = *pEndOfStr;//交換
*pEndOfStr = cCharT;
pWideCharStr++;
pEndOfStr--;
}
return TRUE;
}
EXPORT BOOL StringReserseA(PSTR pMultiByteStr, DWORD cchLength)
{
int nLenofWideChar;
PWSTR pWideCharStr;
BOOL fOk = FALSE;
//計算字元的個數以建立容納Unicode字串
nLenofWideChar = MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, NULL, 0);
//在呼叫程序的預設堆上申請一塊不可移動的記憶體
pWideCharStr = (PWSTR)HeapAlloc(GetProcessHeap(), 0, nLenofWideChar*sizeof(wchar_t));
if (pWideCharStr == NULL)
return fOk;
//轉換為Unicode字串
MultiByteToWideChar(CP_ACP, 0, pMultiByteStr, cchLength, pWideCharStr, nLenofWideChar);
//呼叫StringReverseW函式進行實際的轉換
fOk = StringReverseW(pWideCharStr,cchLength);
if (fOk)
{
//將Unicode再轉換為Ansi
WideCharToMultiByte(CP_ACP, 0, pWideCharStr, cchLength, pMultiByteStr,
(int)strlen(pMultiByteStr), NULL, NULL);
}
//釋放記憶體
HeapFree(GetProcessHeap(), 0, pWideCharStr);
return fOk;
}