在使用strncpy()函數進行字符串的複製時,有時候會出現這樣的情況:目標字符串的最後一個字符是非法字符,導致寫入數據庫時報錯;
【以GBK編碼爲例】原因在於一箇中文字符佔兩個字節,假如需要寫入的字符串長度是30,而恰巧第30位和第31位字節存儲的是漢字,這樣就會將漢字編碼切割開來。然後與之後的字符(例如sql中,後面會加一個單引號)進行組合,形成意料之外的中文字符。
GBK編碼採用雙字節編碼方案,其編碼範圍爲:8140-FEFE,剔除xx7F碼位,共23940個碼位。其中中文字符範圍爲:[0x81-0xFE]。由於漢字是雙字節編碼,如果要滿足該雙字節是漢字的要求的話。則第一個字節範圍必須在0x81-0xFE之間,而第二個字節範圍可以放寬至0x40-0xFE之間。
如果該字符串是以 '|'【0x7C】 作爲分隔符的話,要滿足該分隔符之前是漢字的要求,前面一個字節的編碼必須在【0x81-0xA0,0xA8-0xFE】之間。因爲【0xA1-0xA7】【0x7C】 該雙字節並不組成漢字。具體規則可參考網址:https://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php
代碼如下:
void safe_strncpy(char dest[], char source[], int N)
{
int strlen_source = strlen(source);
int i = 0, j = 0;
int flag = 0; /*添加一個標誌,用來標誌所加的中文的位數 */
unsigned char *p;
/*N取目標串和N的最小值*/
if (strlen_source <N)
N = strlen_source ;
p = (unsigned char *)source;
for (i = 0; i<N; i++) /*N-1用來騰出一位存 '\0 ',字符串數組需要 */
{
if (p[i] >= 0x81 && p[i] <= 0xFE) /*中文字符[0x81-0xFE] */
{
if ((i + 1) == N)
{
/* 前面一半是中文,後面是結束符,捨棄半個漢字*/
dest[j++] = '\0';
}
else
{
if (p[i + 1] >= 0x40 && p[i + 1] <= 0xFE)
{
if (p[i + 1] == 0x7C && ((p[i]<0x81 && p[i]>0xFE) || (p[i]>0xA0 && p[i]<0xA8)))
{
/* 如果是以|爲分隔符的,要滿足前面一半是中文,字節範圍必須在【0X81-0XA0,0XA8-0XFE】之間。如果不在捨棄前面一個 */
i++;
dest[j++] = p[i];
}
else
{
/* 前面一半是中文,後面一半也是中文,說明是一個漢字*/
dest[j++] = p[i];
i++;
dest[j++] = p[i];
}
}
else
{
/* 前面一半是中文,後面一半非中文,捨棄前面一個 */
i++;
dest[j++] = p[i];
}
}
}
else
{
/* 普通的非中文字符 */
dest[j++] = p[i];
}
}
dest[j] = '\0'; /*字符串最後賦值結束符*/
return;
}