UTF-8和中文字符编码(GB2312、GBK、GIB5、GB18030)的识别

来源:互联网 发布:阿里云重新安装系统 编辑:程序博客网 时间:2024/05/29 01:55
1、编码方式介绍
了解一种字符集编码主要是要了解该编码的编码范围,编码对应的字符集(都包含哪些字符),和其他字符集编码之间的关系等。
ASCII
ASCII码是7位编码,编码范围是0x00-0x7F。ASCII字符集包括英文字母、阿拉伯数字和标点符号等字符。其中0x00-0x1F和0x7F共33个控制字符。[1]
只支持ASCII码的系统会忽略每个字节的最高位,只认为低7位是有效位。HZ字符编码就是早期为了在只支持7位ASCII系统中传输中文而设计的编码。早期很多邮件系统也只支持ASCII编码,为了传输中文邮件必须使用BASE64或者其他编码方式。
utf-8
utf-8编码的判断格式如下:
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编码格式
GB2312
GB2312是基于区位码设计的,区位码把编码表分为94个区,每个区对应94个位,每个字符的区号和位号组合起来就是该汉字的区位码。区位码一般 用10进制数来表示,如1601就表示16区1位,对应的字符是“啊”。在区位码的区号和位号上分别加上0xA0就得到了GB2312编码。
区位码中01-09区是符号、数字区16-87区是汉字区,10-15和88-94是未定义的空白区。它将收录的汉字分成两级:第一级是常用汉字 计3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于56-87区,按部首/笔画顺序排列。一级汉 字是按照拼音排序的,这个就可以得到某个拼音在一级汉字区位中的范围,很多根据汉字可以得到拼音的程序就是根据这个原理编写的。
GB2312字符集中除常用简体汉字字符外还包括希腊字母、日文平假名及片假名字母、俄语西里尔字母等字符,未收录繁体中文汉字和一些生僻字。可以用繁体汉字测试某些系统是不是只支持GB2312编码。
GB2312的编码范围是0xA1A1-0xFEFE,去掉未定义的区域之后可以理解为实际编码范围是0xA1A1-0xF7FE(注意:实际编码值是前面的区、位值 + A0)。
GBK
GBK编码是GB2312编码的超集,向下完全兼容GB2312,同时GBK收录了Unicode基本多文种平面中的所有CJK汉字。同 GB2312一样,GBK也支持希腊字母、日文假名字母、俄语字母等字符,但不支持韩语中的表音字符(非汉字字符)。GBK还收录了GB2312不包含的 汉字部首符号、竖排标点符号等字符。
GBK的整体编码范围是为0x8140-0xFEFE,不包括低字节是0×7F的组合。高字节范围是0×81-0xFE,低字节范围是0x40-7E和0x80-0xFE。
字节是0x40-0x7E的GBK字符有一定特殊性,因为这些字符占用了ASCII码的位置,这样会给一些系统带来麻烦。
有些系统中用0x40-0x7E中的字符(如“|”)做特殊符号,在定位这些符号时又没有判断这些符号是不是属于某个 GBK字符的低字节,这样就会造成错误判断。在支持GB2312的环境下就不存在这个问题。需要注意的是支持GBK的环境中小于0x80的某个字节未必就 是ASCII符号;另外就是最好选用小于0×40的ASCII符号做一些特殊符号,这样就可以快速定位,且不用担心是某个汉字的另一半。Big5编码中也存在相应问题。
CP936和GBK的有些许差别,绝大多数情况下可以把CP936当作GBK的别名。
GB18030
GB18030编码向下兼容GBK和GB2312,兼容的含义是不仅字符兼容,而且相同字符的编码也相同。GB18030收录了所有Unicode3.1中的字符,包括中国少数民族字符,GBK不支持的韩文字符等等,也可以说是世界大多民族的文字符号都被收录在内。
GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,也可以理解为是单字节和双字节混合的变长编码。GB18030编码是变长编码,有单字节、双字节和四字节三种方式。
GB18030的单字节编码范围是0x00-0x7F,完全等同与ASCII;双字节编码的范围和GBK相同,高字节是0x81-0xFE,低字节 的编码范围是0x40-0x7E和0x80-FE;四字节编码中第一、三字节的编码范围是0x81-0xFE,二、四字节是0x30-0x39。
BIG5
Big5是双字节编码,高字节编码范围是0x81-0xFE,低字节编码范围是0x40-0x7E和0xA1-0xFE。和GBK相比,少了低字节是0x80-0xA0的组合。0x8140-0xA0FE是保留区域,用于用户造字区。
Big5收录的汉字只包括繁体汉字,不包括简体汉字,一些生僻的汉字也没有收录。GBK收录的日文假名字符、俄文字符Big5也没有收录。因为Big5当中收录的字符有限,因此有很多在Big5基础上扩展的编码,如倚天中文系统。Windows系统上使用的代码页CP950也可以理解为是对Big5的扩展,在Big5的基础上增加了7个汉字和一些符号。Big5编码对应的字符集是GBK字符集的子集,也就是说Big5收录的字符是GBK收录字符的一部分,但相同字符的编码不同。
因为Big5也占用了ASCII的编码空间(低字节所使用的0x40-0x7E),所以Big5编码在一些环境下存在和GBK编码相同的问题,即低字节范围为0x40-0x7E的字符有可能会被误处理,尤其是低字节是0x5C("/")和0x7C("|")的字符。可以参考GBK一节相应说明。
尽管有些区别,大多数情况下可以把CP950当作Big5的别名。

2、识别编码

点击(此处)折叠或打开

  1. //judge the byte whether begin with binary 10
  2. int Encoder::is_utf8_special_byte(unsigned char c)
  3. {
  4.     unsigned special_byte = 0X02; //binary 00000010 
  5.     if (>> 6 == special_byte) {
  6.         return 1;
  7.     } else {
  8.         return 0;
  9.     } 
  10. }

  11. int Encoder::is_utf8_code(const string& str)
  12. {
  13.     unsigned one_byte = 0X00; //binary 00000000
  14.     unsigned two_byte = 0X06; //binary 00000110
  15.     unsigned three_byte = 0X0E; //binary 00001110 
  16.     unsigned four_byte = 0X1E; //binary 00011110
  17.     unsigned five_byte = 0X3E; //binary 00111110
  18.     unsigned six_byte = 0X7E; //binary 01111110
  19.     
  20.     int utf8_yes = 0;
  21.     int utf8_no = 0;
  22.    
  23.     unsigned char k = 0;
  24.     unsigned char m = 0;
  25.     unsigned char n = 0;
  26.     unsigned char p = 0;
  27.     unsigned char q = 0;
  28.    
  29.     unsigned char c = 0; 
  30.     for (uint i=0; i<str.size();) {
  31.         c = (unsigned char)str[i]; 
  32.         if (c>>== one_byte) {
  33.             i++; 
  34.             continue;
  35.         } else if (c>>== two_byte) {
  36.             k = (unsigned char)str[i+1];
  37.             if ( is_utf8_special_byte(k) ) {
  38.                 utf8_yes++; 
  39.                 i += 2;
  40.                 continue;
  41.             }
  42.         } else if (c>>== three_byte) {
  43.             m = (unsigned char)str[i+1];
  44.             n = (unsigned char)str[i+2];
  45.             if ( is_utf8_special_byte(m)
  46.                     && is_utf8_special_byte(n) ) {
  47.                 utf8_yes++;
  48.                 i += 3;
  49.                 continue;
  50.             }
  51.         } else if (c>>== four_byte) {
  52.             k = (unsigned char)str[i+1];
  53.             m = (unsigned char)str[i+2];
  54.             n = (unsigned char)str[i+3];
  55.             if ( is_utf8_special_byte(k)
  56.                     && is_utf8_special_byte(m)
  57.                     && is_utf8_special_byte(n) ) {
  58.                 utf8_yes++;
  59.                 i += 4;
  60.                 continue;
  61.             }
  62.         } else if (c>>== five_byte) {
  63.             unsigned char k = (unsigned char)str[i+1];
  64.             unsigned char m = (unsigned char)str[i+2];
  65.             unsigned char n = (unsigned char)str[i+3];
  66.             unsigned char p = (unsigned char)str[i+4];
  67. if ( is_utf8_special_byte(k)
  68.                     && is_utf8_special_byte(m)
  69.                     && is_utf8_special_byte(n)
  70.                     && is_utf8_special_byte(p) ) {
  71.                 utf8_yes++;
  72.                 i += 5;
  73.                 continue;
  74.             }
  75.         } else if (c>>== six_byte) {
  76.             k = (unsigned char)str[i+1];
  77.             m = (unsigned char)str[i+2];
  78.             n = (unsigned char)str[i+3];
  79.             p = (unsigned char)str[i+4];
  80.             q = (unsigned char)str[i+5];
  81.             if ( is_utf8_special_byte(k)
  82.                     && is_utf8_special_byte(m)
  83.                     && is_utf8_special_byte(n)
  84.                     && is_utf8_special_byte(p)
  85.                     && is_utf8_special_byte(q) ) {
  86.                 utf8_yes++;
  87.                 i += 6;
  88.                 continue;
  89.             }
  90.         } 

  91.         utf8_no++;
  92.         i++; 
  93.     } 

  94.     printf("%d %d\n", utf8_yes, utf8_no);
  95.     int ret = (100*utf8_yes)/(utf8_yes + utf8_no);
  96.     if (ret > 90) {
  97.         return 1; 
  98.     } else {
  99.         return 0;
  100.     }
  101. }


实现原理:判断网页文本中符合utf-8规则的字数和不符合utf-8规则的字数
如果符合的字数超过90%,则判断为utf-8编码

其次应该是gb2312编码的判断,由于gb2312相对gbk和big5的编码范围要小,所以
在gb2312和gbk和big5之间,应该首先判断该网页文本是否是gb2312
函数实现:

点击(此处)折叠或打开

  1. int Encoder::is_gb2312_code(const string& str)
  2. {
  3.     unsigned one_byte = 0X00; //binary 00000000

  4.     int gb2312_yes = 0;
  5.     int gb2312_no = 0;

  6.     unsigned char k = 0;

  7.     unsigned char c = 0;
  8.     for (uint i=0; i<str.size();) {
  9.         c = (unsigned char)str[i];
  10.         if (c>>== one_byte) {
  11.             i++;
  12.             continue;
  13.         } else if (>= 0XA1 && c <= 0XF7) {
  14.             k = (unsigned char)str[i+1];
  15.             if (>= 0XA1 && k <= 0XFE) {
  16.                 gb2312_yes++;
  17.                 i += 2;
  18.                 continue;
  19.             } 
  20.         }
  21.        
  22.         gb2312_no++;
  23.         i += 2; 
  24.     }

  25.     printf("%d %d\n", gb2312_yes, gb2312_no);
  26.     int ret = (100*gb2312_yes)/(gb2312_yes+gb2312_no);
  27.     if (ret > 90) { 
  28.         return 1;
  29.     } else {
  30.         return 0;
  31. } 
  32. }


实现原理:统计符合gb2312编码特征的字数和不符合gb2312编码特征的字数
如果符合的字数超过90%,则判断该网页文本为gb2312

再者应该判断big5编码,原因是因为gbk的范围要比big5的范围广
函数实现

点击(此处)折叠或打开

  1. int Encoder::is_big5_code(const string& str)
  2. {
  3.     unsigned one_byte = 0X00; //binary 00000000

  4.     int big5_yes = 0;
  5.     int big5_no = 0;

  6.     unsigned char k = 0; 

  7.     unsigned char c = 0; 
  8.     for (uint i=0; i<str.size();) {
  9.         c = (unsigned char)str[i];
  10.         if (c>>== one_byte) {
  11.             i++;
  12.             continue;
  13.         } else if (>= 0XA1 && c <= 0XF9) {
  14.             k = (unsigned char)str[i+1];
  15.             if ( k >= 0X40 && k <= 0X7E
  16.                     || k >= 0XA1 && k <= 0XFE) {
  17.                 big5_yes++;
  18.                 i += 2;
  19.                 continue;
  20.             } 
  21.         }
  22.        
  23.         big5_no++;
  24.         i += 2;
  25.     }

  26.     printf("%d %d\n", big5_yes, big5_no);
  27.     int ret = (100*big5_yes)/(big5_yes+big5_no);
  28.     if (ret > 90) {
  29.         return 1;
  30.     } else {
  31.         return 0;
  32.     } 
  33. }


实现原理同gb2312

最后是gbk的判断
函数实现

点击(此处)折叠或打开

  1. int Encoder::is_gbk_code(const string& str)
  2. {
  3.     unsigned one_byte = 0X00; //binary 00000000

  4.     int gbk_yes = 0;
  5.     int gbk_no = 0;

  6.     unsigned char k = 0;

  7.     unsigned char c = 0;
  8.     for (uint i=0; i<str.size();) {
  9.         c = (unsigned char)str[i];
  10.         if (c>>== one_byte) {
  11.             i++;
  12.             continue;
  13.         } else if (>= 0X81 && c <= 0XFE) {
  14.             k = (unsigned char)str[i+1];
  15.             if (>= 0X40 && k <= 0XFE) {
  16.                 gbk_yes++;
  17.                 i += 2;
  18.                 continue;
  19.             } 
  20.         }
  21.        
  22.         gbk_no++;
  23.         i += 2; 
  24.     }

  25.     printf("%d %d\n", gbk_yes, gbk_no);
  26.     int ret = (100*gbk_yes)/(gbk_yes+gbk_no);
  27.     if (ret > 90) { 
  28.         return 1;
  29.     } else {
  30.         return 0;
  31.     } 
  32. }


实现原理同gb2312和big5
3、各代码之间的相互转换
在linux下可以利用iconv实现转换,参考
http://blog.chinaunix.net/uid-29594401-id-4257317.html
0 0
原创粉丝点击