基於UTF8字符串檢查錯誤替換功能

utf8的長度爲1~4個字節,是一種變長串,在轉換和傳送過程中,可能由於某種意外會導致串出現錯誤的字符,致使有些工具無法識別而出現亂碼,或者直接導致操作無法完成。

實際應用中的例子:遊戲中的郵件系統,一般會限制標題,內容的長度,但由於客戶端截取長度時的不正當操作,導致utf8串被從中間截斷。比如標題長度上限是15個字節,然後玩家輸入的標題佔用了16個字節,而最後輸入的一個漢字佔用3個字節,如果直接按長度截斷就會出現最後一個漢字被截掉了一個字節。數據傳到服務端之後,服務端存庫時,數據庫會進行字符檢查,就會發現有不正確的字符,而導致插入數據失敗。

解決辦法:首先客戶端那邊字符截斷的方法要按照utf8的格式來進行過濾截取,比如上面的例子,最後應該截取3個字節而不是1個。對於服務端這邊,爲了保證數據能正確插入,可以對不正確字符進行替換,比較合適的方法就是單個字節的字符替換,可以的話增加日誌記錄,便於查找錯誤根源。客戶端的截取代碼可以參考下面的服務端替換代碼來寫。

// 獲取utf8字符長度
int GetUTF8StrLen(IN const char chHead)
{
	if (0 == (0x80 & chHead))
	{
		return 1;
	}
	// 兩個字節 110xxxxx 10xxxxxx
	else if (0xc0 == (0xe0 & chHead))
	{
		return 2;
	}
	// 三個字節 1110xxxx 10xxxxxx 10xxxxxx
	else if (0xe0 == (0xf0 & chHead))
	{
		return 3;
	}
	// 四個字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
	else if (0xf0 == (0xf8 & chHead))
	{
		return 4;
	}
	return 0;
}

// 10xxxxxx
bool IsUTF8SubChar(IN const char chSubChar)	
{

	return (0x80 == (0xc0 & chSubChar));
}

//////////////////////////////////////////////////////////////////////////
// UTF8字符串檢查,注意pszString必須是utf8編碼的字符串, chReplace爲錯誤編碼字符的替代符
// 返回值標識有沒有字符被替換
bool UTF8Check(IN OUT char* pszString, const char chReplace /* = ' ' */)
{
	bool bHadReplaced = false;
	int nStrLen = strlen(pszString);
	if(nStrLen <= 0) return ;
	for (int i = 0; i < nStrLen;)
	{
		char& chTmp = *(pszString + i);
		int nSubLen = GetUTF8StrLen(chTmp);
		// 頭字節錯誤
		if (0 == nSubLen)
		{
			chTmp = chReplace;
			i++;
			bHadReplaced = true;
		}
		// 1字節長度只要結構正確就沒問題不用檢查
		else if(1 == nSubLen) i++;
		// 超過1字節長度就要檢查後面字節結構
		else if (nSubLen > 1)
		{
			// 剩下字節長度不夠,全部替換
			if ((i + nSubLen) > nStrLen)
			{
				for (int k = i; k < nStrLen; k++) *(pszString + k) = chReplace;
				bHadReplaced = true;
				break;
			}

			int j = 1;
			for (; j < nSubLen; j++)
			{
				char& chSubChar = *(pszString + i + j);
				// 不是正確的子字符,就替換當前chTmp對應的utf8字符
				if (false == IsUTF8SubChar(chSubChar))
				{
					nSubLen = j + 1;
					// 如果當前字符是下一個utf8頭字符,就不替換當前字符
					if (GetUTF8StrLen(chSubChar) > 0) nSubLen--;
					for (int k = 0; k < nSubLen; k++) *(pszString + k + i) = chReplace;	
					bHadReplaced = true;
					break;
				}
			}
			i += nSubLen;
		}
	}

	return bHadReplaced;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章