这里所说的搜索引擎主要是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(参见http://www.chi2ko.com/tool/CJK.htm)
那么相对应的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部分为一小节,考察一段纯汉字关键词,会得出以下结论。
- UTF-8格式的小节数为3的倍数,而GB2312的小节数为2的倍数。
- UTF-8格式的每小节首位为89AB,而GB2312得每小节首位为A-F。
- 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?