這裏所説的搜索引擎主要是Google,因為像百度這種搜索引擎,老老實實的只使用GB2312編碼,自然不存在識別的問題。而Google本來也很厚道的會在Url中標識一下關鍵詞的編碼類型,可惜得加一個修飾“有時”,而另外一些時候,它會十分體貼的自動識別轉化而不告訴你……

本着“Google可往,我亦可往”的信念,自己動手解決關鍵字編碼自動識別的問題。

好在需要識別的範圍已經限定了:GB2312 or UTF-8。那麼當然要從瞭解這兩種編碼的編碼格式下手。網上資源很多,摘其精華。

兩種編碼的格式限定:

GB2312:

每個漢字及符號以兩個字節來表示。第一個字節稱為“高位字節”,第二個字節稱為“低位字節”。
“高位字節”使用了0xA1-0xF7(把01-87區的區號加上0xA0),“低位字節”使用了0xA1-0xFE(把01-94加上0xA0)。

UTF-8:

UTF-8是一種變長字節編碼方式。對於某一個字符的UTF-8編碼,如果只有一個字節則其最高二進制位為0;如果是多字節,其第一個字節從最高位開始,連續的二進制位值為1的個數決定了其編碼的位數,其餘各字節均以10開頭。UTF-8最多可用到6個字節。
如表:
1字節 0xxxxxxx
2字節 110xxxxx 10xxxxxx
3字節 1110xxxx 10xxxxxx 10xxxxxx
4字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字節 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字節 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

而在UTF-8中,漢字都是採用3字節編碼的,即首位為1110xxxx,其餘兩位為10xxxxxx。

考慮UTF-8和Unicode的漢字編碼轉換關係:將Unicode的兩字節二進制編碼拆成4、6、6位,分別插入模板1110xxxx 10xxxxxx 10xxxxxx 中,即為對應的UTF-8漢字編碼。

例如“漢”字的Unicode編碼是6C49。將6C49的二進制:0110 110001 001001, 放入模板中,得到:11100110 10110001 10001001,即E6 B1 89。

而Unicode中的漢字編碼範圍為4E00~9FA5

那麼相對應的UTF-8的範圍是E4A880~E9BEA5

 

下面再簡單看下經過URL編碼後的兩種編碼格式的結果:

“博客”:

  • UTF-8編碼(16進制):E58D9A E5AEA2
  • UTF-8 + Url編碼:%E5%8D%9A%E5%AE%A2
  • GB2312編碼:B2A9 BFCD
  • GB2312 + Url編碼:%B2%A9%BF%CD

結論很清楚,Url編碼就是在兩種編碼格式的16進制基礎上拆分每個字節並加上‘%’前綴

自動識別的思路:

那麼基於以上的成果,可以找出UTF-8和GB2312在URL編碼後的特點和不同,為了説明方便,這裏稱形如’%xx ’的URL部分為一小節,考察一段純漢字關鍵詞,會得出以下結論。

  1. UTF-8格式的小節數為3的倍數,而GB2312的小節數為2的倍數。
  2. UTF-8格式的每小節首位為89AB,而GB2312得每小節首位為A-F。
  3. UTF-8格式的每個字被劃為三小節,且第一小節的首位為E,次位為4~9。

那麼考慮如下自動識別的思路:先獲取URL中所有的小節數x,然後獲取所有以E開頭的每三小節數y,比較x與3y,如果兩者不相等,則説明不是UTF-8編碼。

這裏注意,考慮到一些特殊符號同樣會被Url編碼成“%xx ”的形式,比如‘%’會被編碼成“%25”,所以每小節的特徵應當是首位>=7。

解決方法:

其實最終解決方法就是兩條正則表達式:

String utfRegexPattern = @"(%e[4-9])(%[89ab][0-9a-f]){2}";
String regexPattern = "(%[89a-f][0-9a-f])";
MatchCollection utfMatches = Regex.Matches(url, utfRegexPattern);
MatchCollection matches = Regex.Matches(url, regexPattern);
if (matches.Count != utfMatches.Count * 3)
{
    encoding = Encoding.GetEncoding("GB2312");
}

第一條正則匹配符合Utf-8編碼特徵的每三小節,而第二條則匹配每一小節。

 

注:

這種方法只能判斷一串url不是Utf-8編碼,但不能確定一串編碼是utf-8編碼,比如:%E5%B0%B4%E6%B4%B0,可以是GB2312的“灝存窗”,也可以是utf-8的“尷洰”,應用上述方法並不能判斷它到底是哪種編碼格式。google將其識別為了utf-8格式:

http://www.google.cn/search?hl=zh-CN&q=%E5%B0%B4%E6%B4%B0

但Google也有識別失誤的時候:

http://www.google.cn/search?hl=zh-CN&q=%D1%A7%CF%B0

%D1%A7%CF%B0是GB2312編碼的“學習”,但被Google判斷為Utf-8格式。而用上述方法可以進行識別。

也許Google是利用字符集做匹配判斷的?Who knows?