一、前言

在跨平台、跨操作系统或者跨区域之间,经常会涉及到编码的问题,因为前段时间在项目中,遇到了因为编码而产生乱码的问题,以前对编码也是一知半解,所以决定对编码有一个更为深入的了解,因此才有了这篇自己对编码总结的文章。

二、常见编码

1.ASCII:American Standard Code for Information Interchange(美国信息交换标准代码)的缩写,表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符,这应该是我们最先接触的编码。ASCII中

使用一个字节7位二进制数来表示一个字符(最高位用来进行奇偶检验),因为来7个二进制来表示一个字符,最多也就能表示128个字符。其中0~32以及127位用来表示一些特殊的按键或者其他命令,例如回车键、换页键等等,33~47、123~126

和58~64表示一些标点等特殊符号,剩下的是表示26个字母的大小写和阿拉伯数字。这样的话明显不能满足其他不说英文的国家,例如我们中国,ASCII就无法表示汉字,因此就产生其他的编码。

2.gb2312:既然ASCII无法满足我们无法表示汉字,那么我们聪明的中国人就想出了新的方式来表示汉字,因此,gb2312就诞生了。gb2312中的gb是国标的缩写,由中国国家标准总局发布。gb2312中规定一个汉字由2个字节表示,字母、

数字等沿用ASCII中的表示方式,这样就会产生另外一个问题。例如,“希沃”的希对应的gb2312编码是11001111 10100011,假如最高位不为1的话,如01001111 10100011,则无法区分01001111 10100011是表示“希沃”的希还是O和

#,因此规定表示汉字的两个字节的最高位必须为1,“希沃”的希就表示为11001111 10100011,这样就英文和中文就可以统一进行编码了。同时gb2312对汉字进行了分区,统称为区位码。分区是按照汉字的拼音顺序来分区的,每个区包含

16*6,2个字,例如“希沃”的希位于47区第一行第三列,如下图,区位号4703,用二进制表示则为00101111 00000011,ISO2022规定每个汉字的区号和位号必须分别加上32得到交换码,则交换码为01001111 00100011,又根据前面所提

到的表示汉字的两个字节的最高位必须为1,所以最终得到的“希沃”的希的gb2312编码为11001111 10100011.

但是一些冷僻字等等没有包含在区位码中,例如“﨡 ”,gb2312无法表示。为了表示这些部分字,国人对交换码进行了扩充(不是区域码),因此,GBK编码诞生了.

3.GBK与GB18030:ASCII是gb2312的一字子集,gb2312是GBK的一个子集,它们是向下兼容的。GBK编码规范跟gb2312一样采用双字节编码,在gb2312的区位码上进行了扩充,gb2312的编码在GBK中还是保持不变。例如“希沃”的希

在gb2312中是11001111 10100011,在GBK中也是11001111 10100011。GBK与gb2312表现出来的最大的区别是它能表示的字符的范围更大,包含繁体字、特殊符号等等,这是因为在GBK中低字节最高位都可能不是1,而gb2312中的高字节与

低字节的最高位都必须为1。GB18030则是对GBK的进一步扩展,其中加入了韩语、蒙语等等。但是世界语言多种多样,GBK和GB18030的通用性范围受到了很大的限制。

4.Unicode:Unicode也是一种字符编码方法,不过它只与ASCII兼容。它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。CS只是规定如何

编码,并没有规定如何传输、保存这个编码。例如“希沃”的希字的UCS编码是5E0C,我可以用4个ASCII数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E5 B8 8C来表示它。因此UTF家族就出现了,我们所熟悉的UTF-8、

UTF-16、UTF-32等等,其中UTF是“UCS Transformation Format”的缩写。UCS有两种格式UCS-4和UCS-2,顾名思义就是2个字节与4个字节表示,这里只讨论UCS-2(因为目前UCS-4中的前两个字节都为0,即传说中的BMP,去掉前面两个

为0的字节,则为UCS-2)。如下面的表格,UCS-2与UTF-8、UTF-32与UTF-8之间的对应关系。

UCS-2编码(16进制)

UTF-8 字节流(二进制)

0000 - 007F

0xxxxxxx

0080 - 07FF

110xxxxx 10xxxxxx

0800 - FFFF

1110xxxx 10xxxxxx 10xxxxxx

UTF-32字节流(二进制)

UTF-8 字节流(二进制)

0x00000000 - 0x0000007F 0xxxxxxx
0x00000080 - 0x000007FF  110xxxxx 10xxxxxx
0x00000800 - 0x0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
0x00010000 - 0x001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0x00200000 - 0x03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0x04000000 - 0x7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

我们可以发现这个IP得分址算法很相似。例如,“希沃”的希5E0C,处于0800 - FFFF之间,采用1110xxxx 10xxxxxx 10xxxxxx,三个字节表示,将0C5E改为二进制为: 1001 1110 0000 1100,将二进制插入模板中,得到11101001 1011

1000 10001100,即E5 B8 8C。UTF-32转换为UTF-8也是通过对应表,以同样的方式进行转换。

    5.ANSI:在windows系统中,记事本保存时会默认选择ANSI编码,那ANSI是什么编码呢?ANSI为当前操作系统中的默认编码,例如简体中文操作系统中ANSI为gb2312或GBK,中文繁体操作系统下为big5,日文操作系统下为JIS等。

三、BOM

     上面已经提到了gb2312、GBK、UTF-8、UTF-16等编码,那当我将文本选择不同的编码保存后,它们是怎么样进行保存的呢?首先,简单介绍下,big endian和little endian,例如“希沃”的希Unicode编码为5E0C,当我保存希字的时候,

若以5E0C保存,则为big endian,若以0C5E保存,则为little endian。

Unicode规范中推荐的标记字节顺序的方法是BOM(Byte Order Mark),在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的UTF-16编码是FEFF,UTF-8编码为EF BB BF。而FEFF和EF BB BF在对应的编码中是不存

在的字符,所以不应该出现。在windows中,用记事本保存文件时,可以选择四种编码:ANSI、UTF-8、UTF-16以及UTF-16 big endian,一段文字使用UTF-16编码进行保存时,windows会自动在文件头写入FF FE,若选择UTF-16 big

endian时,windows会自动在文件头写入FE FF,选择UTF-8时会自动写入EF BB BF,选择ANSI时不做任何处理,直接保存。因此,在读取文件时,若文件头出现FF FE时,则文件编码为UTF-16,为FE FF为UTF-16 big endian,为 EF BB BF时

为UTF-8,否则为ANSI,系统默认的编码。

下面我们来验证一下文件是不是这样保存的。

                                                                        

如上图,左图为记事本输入“希沃”两字,“希沃”对应的编码为0C5E,分别以ANSI、UTF-16、UTF-16 BIG ENDIAN、UTF-8进行保存,右图为保存之后对应的十六进制内容。

首先,我们来验证UTF-16编码进行保存时,windows会自动在文件头写入FF FE。在UltraEdit中输入“希沃”两个字,分别选择编码为UTF-16和UTF-16无BOM保存,然后打开文件进行比较,如下图左为内容比较,右图为十六进制比较,

                                                                        

通过比较发现,当选择UTF-16无BOM编码与UTF-16时,在文件开头少了FF FE两个字节,当我们打开编码为UTF-16的文件时,因为文件开头有FF FE两个字节,因此将其当做是UTF-16编码,获取到正确的“希沃”两个字,而当打开文件

编码为UTF-16无BOM的文件时,由于文件开头没有FF FE两个字节以及其他特殊的字节,因此将其打开时,被认为是系统ANSI编码,因此显示为乱码。通过上面的比较我们可以得出:windows在选择UTF-16编码保存文件时,会在文件头写

入FF FE的两个字节。文件开头为FF FE 开头时,会被当做是UTF-16编码来解析。

接下来,我们来验证UTF-8、UTF-16 big endian、ANSI编码是怎样保存的。

                                                                            

从上面两图我们可以看出来,windows在选择UTF-8编码保存文件时,会在文件头写入EF BB BF的三个字节。UTF-16 BIG ENDIAN 会在文件头加入 FE FF两个字节。

综上所述,windows在保存文件时,默认会在文件头写入BOM,UTF-8在文件头写入EF BB BF,UTF-16在文件头写入FF FE,UTF-16 BIG ENDIAN在文件头写入FE FF,ANSI写入BOM。

总结

本章主要是讲述了常见的编码ASCII、gb2312、GBK、UTF-8、UTF-16等编码,以及怎样通过BOM来获取文件的编码。

编码总结,以及对BOM的理解的更多相关文章

  1. 字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8

    原作者:阮一峰(ruanyifeng.com),现重新整理发布,感谢原作者的无私分享. 1.引言 今天中午,我突然想搞清楚 Unicode 和 UTF-8 之间的关系,就开始查资料. 这个问题比我想象 ...

  2. 编码(2)从字节理解Unicode(UTF8/UTF16)

    https://www.cnblogs.com/zizifn/p/4716712.html 从字节理解Unicode(UTF8/UTF16) 如果你不知道或者不了解什么是Unicode/UTF8/UT ...

  3. 常见编码和编码头BOM

    ANSI(American National Standards Institute,美国国家标准学会)ANSI编码标准是指所有从基本ASCII码基础上发展起来的编码标准,比如扩展的ASCII码(12 ...

  4. python3中的编码与解码(超好理解)

    编码和解码是针对数据而言的,数据能干什么呢?无非就是用来显示,储存和传输的: 储存和传输数据当然是希望数据越小越好,所以发明了utf-8这种数据编码显示:它智能将英文用一个字节表示,欧洲的字符用两个字 ...

  5. 关于BOM的理解

    BOM提供了很多对象,用于访问浏览器的功能,这些功能与网页内容无关 BOM的核心对象时window,她表示浏览器的一个实例 window的双重角色   1.JS访问浏览器窗口的一个接口  2.ECMA ...

  6. (转载)字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8

  7. Unicode规范中的BOM 和 ISO8891-1编码

    Unicode规范中的BOM Unicode规范中有一个BOM的概念.BOM——Byte Order Mark,就是字节序标记.在这里找到一段关于BOM的说明: 在UCS 编码中有一个叫做" ...

  8. 签名、BOM头、编码、Windows记事本编码、java编码解码的那些事

    对于Windows记事本: ANSI :GB2312 java中应使用GBK解码 Unicode :有签名的UTF-16LE java中应使用UTF-16解码 Unicode big endian : ...

  9. [py]编码-强力理解版

    py编码骨灰级总结 思路: python执行py文件步骤--py2/3定义变量时unicode差异 1,py2 py3执行py文件的步骤 2,py2 定义变量x='mao' 1.x='mao', # ...

随机推荐

  1. docker could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

    原因: 无法进行网络分配了 解决方法: // 所有的网络 docker network ls // 删除不用的,腾出多余的 docker network rm <networkname> ...

  2. 几个开源ssg 技术方案

    1. Nanoc    2. Middle Man App   3. Hexo   4. DocPad   5. Hugo   6. Jekyll   7. Octopress   8. Harp   ...

  3. C#机器学习插件 ---- AForge.NET

    目录 简介 主要架构 特点 学习之旅 简介 AForge.NET是一个专门为开发者和研究者基于C#框架设计的,这个框架提供了不同的类库和关于类库的资源,还有很多应用程序例子,包括计算机视觉与人工智能, ...

  4. docker-tomcat

    https://yq.aliyun.com/articles/6894 1,add命令源目录 只能从 ./ 当前目录开始,目标目录是容器的目录,不是服务器的目录 2,server.xml改的东西,里面 ...

  5. bzoj1588[HNOI2002]营业额统计——双向链表

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1588 简单Splay.但用双向链表做.很好的思路. 1.(离线)按值排序,记下pre和nxt ...

  6. (转)Android性能优化——工具篇

    Android性能优化是Android开发中经常遇见的一个问题,接下来将对Android性能优化方面的知识点做一个简单的梳理和总结,将从工具和代码两方面进行梳理.所谓工欲善其事必先利其器,本文首先来看 ...

  7. jdk1.8新特性之方法引用

    方法引用其实就是方法调用,符号是两个冒号::来表示,左边是对象或类,右边是方法.它其实就是lambda表达式的进一步简化.如果不使用lambda表达式,那么也就没必要用方法引用了.啥是lambda,参 ...

  8. 试玩swoole扩展 第一天

    安装 pecl install swoole 部分过程: configure: creating ./config.statusconfig.status: creating config.hrunn ...

  9. Netty--JDK序列化编解码传输对象

    使用JDK序列化不需要额外的类库,只需要实现Serializable即可,但是序列化之后的码流只有Java才能反序列化,所以它不是跨语言的,另外由于Java序列化后码流比较大,效率也不高,所以在RPC ...

  10. 怎么使用ping命令进行连通性测试

    关于ping命令的作用: ping 命令有助于验证网络层的连通性!一般进行网络故障排除时,可以使用ping 命令向目标计算机或IP地址发送ICMP回显请求,目标计算机会返回回显应答,如果目标计算机不能 ...