目录

字符编码

本文介绍了常用的字符编码,以及收集了几个查看字符编码的工具网站。

ASSIC码

5be973b473b2a.png

Unicode码

Unicode也称为统一码、万国码,它希望统一所有国家的字符编码,定义了每个字符对应的二进制数(参考:Unicode表 ),可以容纳 100 多万个符号。

那么如何对这个二进制数进行编码存储呢?目前 Unicode 的编码方案有三种,分别是:UTF-8UTF-32UTF-16

UTF-8码

  • 变长编码

  • 对于单字节的符号,字节的第1位设为0,后面7位为这个符号的unicode

  • 对于n(n>=2)字节的符号,第1个字节的前 n 位为1,n+1 位为0; 后面字节前2位为10。剩下的二进制位都是这个符号的unicode

  • UNIX 家族的操作系统(Linux、Mac OS、iOS 等)内核都采用 UTF-8 编码

Unicode 编码范围    |        UTF-8 编码存储方式
(十六进制)          |       (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx                                x有7 位,能存下0~0x7F
0000 0080-0000 07FF | 110xxxxx 10xxxxxx                       x有11位,能存下0~0x7FF
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx              x有16位,能存下0~0xFFFF
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx     x有21位,能存下0~0x10FFFF

UTF-32

  • 固定长度编码

  • 始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储,不需要任何编码转换

UTF-16

  • UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储

  • 对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。

  • 对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储

  • Windows 内核、.NET Framework、Cocoa、Java String 内部采用的都是 UTF-16 编码

其他编码

GB2312码

用两个字节表示一个汉字,所以理论上最多可以表示65536个汉字,但是没用完,只定义了6763个常用汉字和682个全角的非汉字字符。

GBK码

也是两个字节,共收入21886个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号883 个。

UTF的字节序和BOM

  • UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

  • Unicode规范中推荐的标记字节顺序的方法是BOM。BOM是Byte Order Mark。BOM是一个有点小聪明的想法:

    • 在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-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

  • Windows就是使用BOM来标记文本文件的编码方式的。

"联通" 两个汉字乱码问题

  • 现象: 当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!

分析

  • windows默认保存的编码是GBK,编码如下

c1 aa 11000001 10101010
cd a8 11001101 10101000
  • 巧合的地方在于联通这两个字的GBK编码符合utf8编码的第二个模板 0000 0080-0000 07FF | 110xxxxx 10xxxxxx

  • 于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因

  • 可以认为,当文档中的所有字符的二进制编码在C0≤AA(第一个字节)≤DF 80≤BB(第二个字节)≤BF时,记事本都无法确认文本的编码格式,就按照UTF-8的格式来显示

业界解决上述问题的办法 -- 使用BOM

  • 当我们在记事本编写"联通",然后另存为,选择UTF-8的格式,保存。再重新打开的时候,就不会有乱码。用UltraEdit的十六进制打开可以看到 EF BB BF E8 81 94 E9 80 9A。其中 EF BB BF 是UTF-8编码格式的标记。当用记事本打开文件时,读到EF BB BF 时,就确定这是UTF-8编码格式的字符。所以打开时不会看到乱码。这个EF BB BF就是BOM,就是用于识别文件编码的标记头的意思

MBCS (Multi-Byte Chactacter System 多字节字符系统) 通常也称为ANSI字符集

  • MBCS文本没有这些开头的字符集标记。更不幸的是,一些早期的和一些设计不良的软件在保存Unicode文本时不插入这些位于开头的字符集标记。

  • 因此,软件不能依赖于这种途径。这时,软件可以采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户,例如将那个“连通”文件拖到MS Word中,Word就会弹出一个对话框。

  • 如果软件不想麻烦用户,或者它不方便向用户请示,那它只能采取自己“猜”的方法,软件可以根据整个文本的特征来猜测它可能属于哪个charset,这就很可能不准了。使用记事本打开那个 "联通" 文件就属于这种情况。

PHP文件带BOM头带来的问题

  • 类似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码

  • 对于 PHP来说,BOM是个大麻烦。

  PHP并不会忽略BOM,所以在读取、包含或者引用这些文件时,会把BOM作为该文件开头正文的一部分。根据嵌入式语言的特点,这串字符将被直接执行(显示)出来。由此造成即使页面的 top padding 设置为0,也无法让整个网页紧贴浏览器顶部,因为在html一开头有这3个字符呢!

 window编辑器如果保存为utf8文件就会帮你加上BOM头,以告诉其他编辑器以utf8来显示字符

但是在网页上并不需要添加BOM头识别,因为网页上可以使用 head头 指定charset=utf8告诉浏览器用utf8来解释.但是你用window自动的编辑器,编辑,然后有显示在网页上这样就会显示出0xEF 0xBB 0xBF这3个字符
附:再来一段议论utf8的BOM信息的
BOM是指php文件本身的存储方式为带BOM的UTF-8,普通页面的中文乱码方式一般不是由这个原因导致的。

header("Content-type: text/html; charset=utf-8");
这句话控制html输出页面的编码方式,

BOM只有在WINDOWS下采用“记事本”存储为UTF-8时才会有,这个可以用WINHEX把开始的2个字节删掉。
在dreamweaver里面编码设置里面可以设置是否带BOM,一般只要php输出的不是图片(GDI Stream),BOM都不会导致问题。
GDI Stream如果开头有了额外的 字符就会显示为 红叉。

查询汉字编码

WechatIMG4.png

2019-05-04 19-08-52 的屏幕截图.png

参考