字符集相关知识
什么是字符集?
字符集又称字符编码,在计算机中所有数据都是二进制形式,包括 a b c @ # $。字符编码规定了用哪些二进制数表示哪些符号。
ASCII编码
单字节表示法,有一个扩充bit,另外7bit可以表示128个字符,其中有33个控制和95个可显示字符。
hello world  ASCII 
68 65 6C 6C  6F 20 77 6F  72 6C 64 20  20 41 53 43  49 49 0A 
现代编码模型
抽象字符表
字符集所能表示的所有字符的集合。
编码字符集
将字符集中的每个字符映射到一个坐标{x, y}或是非负整数N,比如ISO-8859-1和代码页037涵盖同样的抽象字符表,但是编码字符集不同。
编码空间可以用一对整数,比如94x94或是一个整数,比如256来描述,编码空间中的一个位置称为码位。
编码字符集的作用就是将抽象字符映射为码位值。
字符编码表
将编码字符集的码位转化为有限比特长度的整型值(码元)。对于定长编码来讲是到自身的映射,对于变长编码,需要将一些码位映射到多个码元组成的序列。
现在的Unicode字符集至少需要21位才能全部表示,UTF-8使用可变长度的8位字节序列表示。
字符编码方案
传输编码语法
用于处理上一层次的字符编码方案提供的字节序列。一般其功能包括两种:一是把字节序列的值映射到一套更受限制的值域内,以满足传输环境的限制,例如Email传输时Base64或者quoted-printable,都是把8位的字节编码为7位长的数据;另一是压缩字节序列的值,如LZW或者行程长度编码等无损压缩技术。
GB2312字符集
GB2312对文字进行了分区处理,一共94个区,每个区包含94个汉字,例如“万”的区位码是 45-82。在存储时使用两个字节表示,第一个字节称为高位字节,对应分区编号,第二个字节称为低位字节,是位码加上特定值。用于和ASCII兼容。收录6763个汉字,同时收录了拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。
GBK字符集
GBK 采用双字节表示,共收入 21886 个汉字和图形符号,包括:
- GB 2312 中的全部汉字、非汉字符号。
 - BIG5 中的全部汉字。
 - 与 ISO 10646 相应的国家标准 GB 13000 中的其它 CJK 汉字,以上合计 20902 个汉字。
 - 其它汉字、部首、符号,共计 984 个。
 
GBK 向下与 GB 2312 完全兼容,向上支持 ISO 10646 国际标准,在前者向后者过渡过程中起到的承上启下的作用。
GB18030字符集
GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个。
- 与 UTF-8 相同,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。
 - 编码空间庞大,最多可定义 161 万个字符。
 - 支持中国国内少数民族的文字,不需要动用造字区。
 - 汉字收录范围包含繁体汉字以及日韩汉字
 
GB 18030 编码是一二四字节变长编码。
Unicode(通用字符集)
Unicode 规定了每个字符对应的唯一编码,从0x0000-0x10FFFF,前两位表示平面,从0-0x10共17个平面,后16位表示字符,每个平面最多65536个字符。对应的二进制有21位。大多数字符在第0个平面,一般用U+开头的4位16进制描述,比如【马】U+9A6C。
虽然一个字符对应的编码是确定的,但是在传输和保存中对于编码有不同的实现方式,称为unicode转换格式(UTF)。如果要一对一映射至少需要21位。
TODO: 是否可以表示所有Unicode字符集
UTF-8
| 
 Code Point Segment 
 | 
 Encoding 
 | 
| 
 0x000000 - 0x00007f 
 | 
 0xxxxxxx 
 | 
| 
 0x000080 - 0x0007ff 
 | 
 110xxxxx 10xxxxxx 
 | 
| 
 0x000800 - 0x00ffff 
 | 
 1110xxxx 10xxxxxx 10xxxxxx 
 | 
| 
 0x010000 - 0x10ffff 
 | 
 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
 | 
不同段的代码点会以不同的长度存储,用不同的前缀表示计算机解码时需要读取的长度,对于西文,该编码方式占用的空间更少。
优点:字符空间大,没有字节序问题,容错性高,可以很容易检测字符边界。
UTF-16
| 
 Code Point Segment 
 | 
 Encoding 
 | 
| 
 0x000000 - 0x00ffff 
 | 
 xxxxxxxx xxxxxxxx 
 | 
| 
 0x010000 - 0x10ffff 
 | 
 110110yy yyyyyyyy 110111xx xxxxxxxx 
 | 
变长编码,对于基本平面用2个字节表示,用扩展平面用4个字节表示。用高位代理
110110和低位代理110111表示扩展平面。不用担心高位代理和低位代理会和基本平面的字符冲突,因为规定从U+D800到U+DFFF之间的区段是永久保留,不映射到unicode字符的。优点:计算字符串长度、字符索引时速度快。
UTF-32
| 
 Code Point Segment 
 | 
 Encoding 
 | 
| 
 0x000000-0x10ffff 
 | 
 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 
 | 
用32个bit对unicode进行编码,前导bit为0,每个32位值代表一个unicode码,并且数值完全一致,可以在常数时间内索引到第N个编码。
UTF-8不存在字节序列问题,占用空间小,适合做网络传输和存储。UTF-16/32适合程序内部计算。
字节顺序标记(BOM)
字节顺序标记通常称为大小端,位于文件最开始的地方,用来标记高位在前 (BE) 还是低位在前 (LE)。
| 
 UTF Encoding 
 | 
 Byte Order Mark 
 | 
| 
 UTF-8 with bom 
 | 
EF BB BF | 
| 
 UTF-16 LE 
 | 
FF FE | 
| 
 UTF-16 BE 
 | 
FE FF | 
| 
 UTF-32 LE 
 | 
FF FE 00 00 | 
| 
 UTF-32 BE 
 | 
00 00 FE FF | 
文件编码检测
| 
 Unicode 
 | 
 UTF-8 
 | 
| 
 UTF-8 with bom 
 | 
|
| 
 UTF-16 LE 
 | 
|
| 
 UTF-16 BE 
 | 
|
| 
 UTF-32 LE 
 | 
|
| 
 UTF-32 BE 
 | 
|
| 
 简体中文 
 | 
 GB2312 
 | 
| 
 GBK 
 | 
|
| 
 GB18030 
 | 
|
| 
 繁体中文 
 | 
 Big5 
 | 
| 
 Big5-HKSCS 
 | 
|
| 
 日语 
 | 
 sjis 
 | 
| 
 jis7 
 | 
|
| 
 EUC-JP 
 | 
|
| 
 朝鲜语 
 | 
 cp 949 
 | 
| 
 EUC-KR 
 | 
- 
通过BOM判断
 
根据文件开头的字节顺序标记判断。
准确,但应用范围较窄
- 
通过编码方案判断
 
在任何编码方案中,并非所有的码位都会被使用,如果在解码时遇到非法字节序列,则可以推断这不是正确的解码方案。
为每个编码方案实现一个状态机来验证一个字节序列是否符合该编码方案。
使用多字节编码,通常可以获得较好的效果。但是对于某些编码,比如GB2312和EUC-KR共享几乎相同的编码点,因此难以仅通过编码方案判断。对于单字节编码,所有代码点使用比较均匀,也不适合用编码方案判断。
- 
字符分配方法
 
根据不同语言的常用字符确定文件编码
GB2312
| Number of Most Frequent Characters | Accumulated Percentage | 
| 10 | 0.11723 | 
| 64 | 0.31983 | 
| 128 | 0.45298 | 
| 256 | 0.61872 | 
| 512 | 0.79135 | 
| 1024 | 0.9226 | 
| 2048 | 0.98505 | 
| 4096 | 0.99929 | 
| 6763 | 1 | 
Big5
| Number of Most Frequent Characters | Accumulated Percentage | 
| 10 | 0.11713 | 
| 64 | 0.29612 | 
| 128 | 0.42261 | 
| 256 | 0.57851 | 
| 512 | 0.74851 | 
| 1024 | 0.89384 | 
| 2048 | 0.97583 | 
| 4096 | 0.9991 | 
Japanese
| Number of Most Frequent Characters | Accumulated Percentage | 
| 10 | 0.27098 | 
| 64 | 0.66722 | 
| 128 | 0.77094 | 
| 256 | 0.8571 | 
| 512 | 0.92635 | 
| 1024 | 0.9713 | 
| 2048 | 0.99431 | 
| 4096 | 0.99981 | 
| 1 | 
Distribution Ratio: 512个常用字符出现次数/其它字符出现次数
一段正常的GB2312中文文本 distribution ratio = 0.79135/(1-0.79135)=3.79
随机生成的中文本文 distribution ratio = 512/(6763-512)=0.157
适合多字节编码仅用于典型文本
- 
双字符序列分配方法
 
利用连续两个字符的出现频率
可以用到更多的信息数据,不适合用于多字节语言。即使样本量很小,也能产生良好的结果仅用于典型文本
屾槸杩欐牱鈥濈綏宄扮暐寰仠椤夸竴涓嬶紝鐐圭偣澶达紝鈥滆繖鏍峰惂锛岃繖涓槦鏈熺殑鏄熸湡澶笅鍗堬
复合方法
双字符序列分配用于单字节编码检测
编码方案用于UTF-8、ISO-2022-xx 和 HZ 检测
代码方案和字符分布方法都用于主要的东亚字符编码,例如 GB2312、Big5、EUC-TW、EUC-KR、Shift_JIS 和 EUC-JP。其中日语编码因为有大量平假名,也可以用双字符序列方法。
Charset AutoDetection (InputText)
{
   if (all characters in InputText are ASCII)
   {
       if InputText contains ESC or “~{“
       {
          call ISO-2022 and HZ detector with InputText;
          if one of them succeed, return that charset, otherwise return ASCII;
       }
       else
          return ASCII;
   }
   else if (InputText start with BOM)
  {
      return UCS2;
  }
  else
  {
      Call all multi-byte detectors and single-byte detectors;
      Return the one with best confidence;
  }
}
字符集检测流程
- 检查文件开头的BOM(如果有)
 - 检查字符是否属于ASCII编码范围
 - 
依次检测三种字符集
- 多字节编码字符集 "UTF-8", "SJIS", "EUC-JP", "GB18030", "EUC-KR", "Big5", "EUC-TW",
 - 单字节编码字符集
 
c. 拉丁字母字符集 windows-1252 
UTF-8编码检测
  for (PRUint32 i = 0; i < aLen; i++)
  {
    codingState = mCodingSM->NextState(aBuf[i]);
    if (codingState == eItsMe)
    {
      mState = eFoundIt;
      break;
    }
    if (codingState == eStart)
    {
      if (mCodingSM->GetCurrentCharLen() >= 2)
        mNumOfMBChar++;
    }
  }
  if (mState == eDetecting)
    if (GetConfidence() > SHORTCUT_THRESHOLD)
      mState = eFoundIt;
  return mState;
}
GB18030编码检测
nsProbingState nsGB18030Prober::HandleData(const char* aBuf, PRUint32 aLen)
{
  PRUint32 codingState;
  for (PRUint32 i = 0; i < aLen; i++)
  {
    codingState = mCodingSM->NextState(aBuf[i]);
    if (codingState == eItsMe)
    {
      mState = eFoundIt;
      break;
    }
    if (codingState == eStart)
    {
      PRUint32 charLen = mCodingSM->GetCurrentCharLen();
      if (i == 0)
      {
        mLastChar[1] = aBuf[0];
        mDistributionAnalyser.HandleOneChar(mLastChar, charLen);
      }
      else
        mDistributionAnalyser.HandleOneChar(aBuf+i-1, charLen);
    }
  }
  mLastChar[0] = aBuf[aLen-1];
  if (mState == eDetecting)
    if (mDistributionAnalyser.GotEnoughData() && GetConfidence() > SHORTCUT_THRESHOLD)
      mState = eFoundIt;
//    else
//      mDistributionAnalyser.HandleData(aBuf, aLen);
  return mState;
}
- UTF8和GB2312什么区别,为啥UTF8转GB2312会有乱码出现
 - GB2312转UTF8会有乱码么
 - UTF8和Unicode又是什么关系
 - UTF8用在什么场景,UTF16用在什么场景,为什么
 - GB2312以及各种本地字符集出现的历史缘由
 - 多字节编码和双字节编码什么区别,和UTF8、UTF16、GB2312又是什么关系
 
参考文献
转载请保留原文链接及作者
    | 本文标题: | |
| 文章作者: | LepeCoder | 
| 发布时间: | |
| 原始链接: | 

                
            
        
浙公网安备 33010602011771号