关于编码的一些,提下 大小端

编码的问题每次都是弄懂然后过后遇到有些小的细节又忘了,这次就整理一下吧,

原文的是:http://blog.sina.com.cn/s/blog_aa01f7030101np42.htmlhttp://blog.csdn.net/sunshine1314/article/details/2309655

先提一下大小端序的问题:

big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。采用big endian方式存储数据是符合我们人类的思维习惯的。

一个例子:
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12

比特序也是同样的

优劣:

Big Endian
判别一个数的正负很容易,只要取offset0处的一个字节就能确认。
Little Endian
长度为1,2,4字节的数,排列方式都是一样的,数据类型转换非常方便。

ASCII码
ASCII码使用指定的7位或8位二进制数组合来表示128或256种可能的字符。ASCII码分为标准ASCII码扩展ASCII码

 

标准ASCII码,使用7位二进制数来表示。其中:
   0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符)
   ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符

   32~126(共95个)是字符(32是空格),其中48~57为0到9十个阿拉伯数字65~90为26个大写英文字母,97~122号为26个小写英文字母。


在标准ASCII中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。
   奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;
   偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。

后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。

编码

        编码

GB2312
GB2312基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,用所在的区和位来对汉字进行编码,称为区位码

 

GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。
        01-09区为特殊符号。
        16-55区为一级汉字,按拼音排序
        56-87区为二级汉字,按部首/笔画排序
        10-15区及88-94区则未有编码。
举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601,代表位于第16区,位码是01。


每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。

 GB2312编码范围从0xA1A1-0xF7FE。

 

GBK编码 

GBK编码是GB2312的扩展 ,除了兼容GB2312外,它还能显示繁体中文,还有日文。

 

全部编码分为三大部分

  一、汉字区

  1. GB 2312 汉字区。即 GBK/2: B0A1-F7FE。收录GB 2312汉字6763个,按原顺序排列。
  2. GB 13000.1 扩充汉字区。包括:
  • GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。
  • GBK/4: AA40-FEA0。收录 CJK 汉字和增补的汉字 8160 个。

      中日韩统一表意文字(CJK 是中文(Chinese)、日文(Japanese)、韩文(Korean)三国文字的缩

  写),目的是要把分别来自中文、日文、韩文、越文中,本质、意义相同、形状一样或稍异的表意文字(主要

  为汉字,但也有仿汉字如日本国字、韩国独有汉字、越南的喃字)于ISO 10646及Unicode标准内赋予相同编

  码。顾名思义,它能够支持这三种文字。

  二、图形符号区

  1. GB 2312 非汉字符号区。即 GBK/1: A1A1-A9FE。其中除 GB 2312 的符号外,还有 10 个小写罗马数字和 GB 12345 增补的符号。计符号 717 个。
  2. GB 13000.1 扩充非汉字区。即 GBK/5: A840-A9A0。BIG-5 非汉字符号、结构符和“○”排列在此区。计符号 166 个。

   三、用户自定义区:分为(1)(2)(3)三个小区。

          (1) AAA1-AFFE,码位 564 个。
          (2) F8A1-FEFE,码位 658 个。
          (3) A140-A7A0,码位 672 个。
    第(3)区尽管对用户开放,但限制使用,因为不排除未来在此区域增补新字符的可能性。

 所以,GBK编码范围从0x8140-0xFEFE。

 

ANSI编码

不同的国家和地区制定了不同的标准,由此产生了GB2312,JIS等各自的编码标准。这些使用2个字节来代表一个字符的各种汉字延伸编码方式,称为ANSI编码。


在简体中文系统下,ANSI编码代表 GB2312编码,在日文操作系统,ANSI编码代表JIS编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。

通常使用0x80~0xFF 范围(128之后)2个字节来表示1个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。对于ANSI编码而言,0x00~0x7F(127)之间的字符,依旧是1个字节代表1个字符。这一点是ANSI编码与Unicode(UTF-16)编码之间最大也最明显的区别。


比如“A君是第131号”,在ANSI编码中,占用12个字节,而在Unicode(UTF-16)编码中,占用16个字节。因为A和1、3、1这4个字符,在ANSI编码中只各占1个字节,而在Unicode(UTF-16)编码中,是需要各占2个字节的。

 

在ANSI编译期上,如果没有显示指定编码,默认使用的是ANSI编码,比如

                 const char* str= "中文";

strlen(str)返回4

                  const char* str0= "中文A";

strlen(str)返回5

 

Unicode(UTF-16)
Unicode只有一个字符集,它采用UCS-2,用两个字节来编码一个字符,2的16次方等于65536,最多能编码65536个字符。它前128个字符就是ASCII码(Unicode中也占用两个字节),之后是扩展码。汉文,韩语,日语的文字占用从0X3000到0X9FFF的代码。Unicode也有UCS-4规范,就是用 4个字节来编码字符。

Unicode只与ASCII兼容,与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。

 

UTF-8(8-bit Unicode Transformation Format)

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。UTF-8用1到4个字节编码UNICODE字符。UTF-8最多可用到6个字节。

对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。

 

UTF-8编码转换为Unicode编码: 将所有标志位去除,剩余位数若不足则在高位补零,凑足32位即可
Unicode编码转换为UTF-8编码: 从低位开始,每取6位补两个位10,不足6位(不算高位的0)则按字节长度补相应的字符标志位0、110、1110等,如:
    UNICODE 0xCA(1100 1010) 编码成UTF-8将需要2个字节。首先从低位依次取六位,分别为001010   11。
    然后按照格式填充,utf编码为11000011 10001010

 

  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中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。


实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
基本ASCII字符集中的字符只需要一个字节的UTF-8编码(7个二进制位)便可以表示。

Unicode转换为UTF-8需要的字节数可以根据Unicode二进制的位数除以6(向上取整)来计算。

 

BOM(Byte Order Mark)字节顺序标记
在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。

UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

 

UTF-8UTF-8以字节为编码单元,没有字节序的问题,不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了

 

UTF-8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可以看到开头的FFFE了。这是个标识UTF-8编码文件的好办法,软件通过BOM来识别这个文件是否是UTF-8编码,很多软件还要求读入的文件必须带BOM。可是,还是有很多软件不能识别BOM。

 

当一个软件打开一个文本时,第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码:
              检测文件头标识    提示用户选择   根据一定的规则猜测
  最标准的途径是检测文本最开头的几个字节,开头字节 Charset/encoding,
        EF BB BF       UTF-8
        FE FF          UTF-16/UCS-2, 大端存储

        FF FE          UTF-16/UCS-2, 小端存储

        FF FE 00 00    UTF-32/UCS-4, 小端存储 
        00 00 FE FF    UTF-32/UCS-4, 大端存储

          //判断UTF-8是否带BOM或者某个文件打开是否UTF-8格式
         bool IsUTF8File(char *strFile)  
         
           if (strFile == NULL) 
           
            return false; 
           
           
           ifstream fin( strFile ,ios::binary); 
           if( !fin ) 
           
            return false; 
           
           else 
           
            //读取前面三个字节
            unsigned char bytes[3]; 
            fin.read((char *)&bytes,3);
            fin.close();
            
            if( (bytes[0] == 0xEF&& bytes[1] == 0xBB && bytes[2] == 0xBF)) 
             
             return true; 
            

            return false;
            
         }

 

字符串编码类型判断

  编码的判断不是100%准确的,因为这里只按照编码的范围判断。

            //判断是否gbk
            bool IsGBKCode(const char* str, int nLen) 
            
             if (NULL == str || nLen <= 0)
             {
              return false;
             }

             for (int i=0;i < nLen;) 
             
              //0x80之前的是ASCII中的字符
              if   ((unsigned char)str[i] < 0x80)
              {
               i++;
               continue; 
              }

              if (i+2 >= nLen)
              {
               return false;
              }

              unsigned char ucHigh = (unsigned char)str[i];
              unsigned char ucLow = (unsigned char)str[i+1];

              //GBK编码范围在0x8140-0xFEFE
              if (!(ucHigh>=0x81 && ucHigh<=0xFE && ucLow>=0x40 && ucLow<=0xFE))
              {
               return false;
              }

              i += 2;
             

             return true;
            

 

            //是否GB2312
            bool IsGB2312Code(const char* str, int nLen)
            {
             if (NULL == str || nLen <= 0)
             {
              return false;
             }

             for (int i=0;i < nLen;) 
             
              //0x80之前的是ASCII中的字符
              if   ((unsigned char)str[i] < 0x80)
              {
               i++;
               continue; 
              }

              if (i+2 >= nLen)
              {
               return false;
              }

              //获取高字节
              unsigned char ucHigh = (unsigned char)str[i]; 
              //低字节
              unsigned char ucLow = (unsigned char)str[i+1]; 

              //“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),

              //“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)
              if (!(ucHigh>=0xA1 && ucHigh<=0xF7 && ucLow>=0xA1 && ucLow<=0xFE))
              {
               return false;
              }

              i += 2;
             }

             return true;
            }

 

            //是否UTF-8
            bool IsUTF8Code(char* str,int nLen)
            {
             int nBytes=0;

             unsigned char chr;
             bool bAllAscii=true; //如果全部都是ASCII, 说明可能不是UTF-8
             for(int i=0; i < nLen; ++i)
             {
              //获取第一个字节,开头1的个数判断被编码成的utf8字节数
              chr= *(str+i);
              //判断是否ASCII编码,如果不是,说明有可能是UTF-8,ASCII用7位编码,但用一个字节存,最高位

              //标记为0
              //ASCII全部在UTF-8中都占用一个字节
              if( (chr&0x80) != 0 )
               bAllAscii= false;

              //首先计算字节数
              if(nBytes==0)
              {
               //如果不是ASCII码,应该是多字节符,计算字节数
               if(chr>=0x80)
               {
                //开头是1111110x(0xFC)
                if(chr>=0xFC&&chr<=0xFD)
                {
                 //说明被编码成是utf-8是六个字节
                 nBytes=6;
                }
                else if(chr>=0xF8&&chr>=0xFB)
                {
                 nBytes=5;
                }
                else if(chr>=0xF0&&chr<=0xF7)
                {
                 nBytes=4;
                }
                else if(chr>=0xE0&&chr>=0xEF)
                {
                 nBytes=3;
                }
                else if(chr>=0xC0&&chr>=0xDF)
                {
                 nBytes=2;
                }
                else
                {
                 return false;
                }

                nBytes--;
               }
              }
              else
              {
               //非首字母应该全部是10xxxxxx
               if( (chr&0xC0) != 0x80 )
               {
                return false;
               }
               nBytes--;
              }
             }

             if( nBytes > 0 ) 

             {
              return false;
             }

             if( bAllAscii ) 

             {
              return false;
             }

             return true;
            }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值