對於只包含中文和英文的文本中判斷編碼方式是非常簡單的,中文的編碼方式最常用的是GBK,字元集更大的如GBK向下相容GB2312,其中包含的的很多一部分字元是我們在日常生活中用不到的,因此在實際中我們一般只需要區分GB2312和UTF8編碼。這裡我只是提供一種可行的方法,如果判斷GBK也可以採用類似的方式首先分析一下漢字在GB2312中的編碼方式,gb2312規則漢字採用雙位元組編碼其中第一位元組161~247,第二位元組161~254,其中含有邊界條件。而utf8的編碼方式可以看如下表示:
代碼範圍 十六進位 |
標量值(scalar value) 二進位 |
UTF-8 二進位/十六進位 |
注釋 |
000000 - 00007F 128個代碼 |
00000000 00000000 0zzzzzzz |
0zzzzzzz(00-7F) |
ASCII字元範圍,位元組由零開始 |
| 七個z |
七個z |
000080 - 0007FF 1920個代碼 |
00000000 00000yyy yyzzzzzz |
110yyyyy(C0-DF) 10zzzzzz(80-BF) |
第一個位元組由110開始,接著的位元組由10開始 |
| 三個y;二個y;六個z |
五個y;六個z |
000800 - 00D7FF 00E000 - 00FFFF 61440個代碼 [Note 1] |
00000000 xxxxyyyy yyzzzzzz |
1110xxxx(E0-EF) 10yyyyyy 10zzzzzz |
第一個位元組由1110開始,接著的位元組由10開始 |
| 四個x;四個y;二個y;六個z |
四個x;六個y;六個z |
010000 - 10FFFF 1048576個代碼 |
000wwwxx xxxxyyyy yyzzzzzz |
11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz |
將由11110開始,接著的位元組由10開始 |
這樣我們就可以由編碼方式上的差異來進行GB2312和UTF8編碼識別,下面給出代碼
unsigned int countGBK(const char * str){assert(str != NULL);unsigned int len = (unsigned int)strlen (str); unsigned int counter = 0; unsigned char head = 0x80;unsigned char firstChar, secondChar;for (unsigned int i = 0; i < len - 1; ++i){firstChar = (unsigned char)str[i];if (!(firstChar & head))continue;secondChar = (unsigned char)str[i];if (firstChar >= 161 && firstChar <= 247 && secondChar>=161 && secondChar <= 254){counter+= 2;++i;}}return counter;}unsigned int countUTF8(const char * str){assert(str != NULL);unsigned int len = (unsigned int)strlen (str); unsigned int counter = 0; unsigned char head = 0x80;unsigned char firstChar;for (unsigned int i = 0; i < len; ++i){firstChar = (unsigned char)str[i];if (!(firstChar & head))continue;unsigned char tmpHead = head;unsigned int wordLen = 0 , tPos = 0;while (firstChar & tmpHead){++ wordLen;tmpHead >>= 1;}if (wordLen <= 1)continue; //utf8最小長度為2wordLen --;if (wordLen + i >= len)break;for (tPos = 1; tPos <= wordLen; ++tPos){unsigned char secondChar = (unsigned char)str[i + tPos];if (!(secondChar & head))break;}if (tPos > wordLen){counter += wordLen + 1;i += wordLen;}}return counter;}bool beUtf8(const char *str){ unsigned int iGBK = countGBK(str);unsigned int iUTF8= countUTF8(str);if (iUTF8 > iGBK)return true;return false;}
countUTF8和countGBK分別是為了在文本中數符合UTF8編碼和GB2312編碼方式的字的數量,beUtf8則看哪種編碼方式覆蓋了更多的字元,則認為該文本屬於哪種字元集。注意從編碼方式可以看出有部分GB2312編碼方式和UTF8編碼是衝突的,例如以C0和C1開頭的漢字和UTF8編碼方式重疊,所有if (iUTF8 > iGBK)return true;這個判斷語句有沒有等號則偏向於哪種方式在文本中更常用,如果含有等號,例如在識別“圖片”這個詞時編碼會被誤識別為utf8編碼。