Fork me on GitHub

关于字符编码

ASCII码表

作为一名程序员,对编码问题有必要了解一下。计算机是以二进制形式存储数据的,至于为什么选择二进制,这可能就需要从计算机的诞生历史说起了,这不是现在关注的点。

计算机文件一般可以分为(暂且这样分吧):文本文件和二进制文件。通俗的说,文本文件就是平常通过某个软件(记事本,EditPlus,UltraEdit,sublime等各种编辑器,各种IDE等等)打开看到是‘字符’,二进制文件就是打开是图片或视频等,当然是通过某些能够解释二进制的程序来完成。
这里需要强调的是,文件的后缀名不能用来区分文件的类型,后缀名只是系统(特别是Windows)用来对文件进行预先分类,并给予对应的图标和相应双击后打开的程序,至于预先给定的程序能不能打开就不得而知。
如,在Windows下,把一个txt文件的后缀名改成jpg后:

变成

双击打不开,用记事本依然能打开,就是内部编码没变,其实文件的类型在编码成二进制形式已经确定(一般是在头部)。

php读取图片文件:

1
2
$a = file_get_contents('bg.png');
echo $a;

浏览器以UTF8形式打开图片的二进制形式,虽然大部分是乱码,但头部还是可以看到解码为PNG的:

字符编码

字符编码就是每个在电脑里出现的字符都会对应一个二进制数,这个二进制可以成为码点(code point)。不同的编码形式,包含的字符数不同,字符和二进制数对应关系也不同。

ASCII码

ASCII(American Standard Code for Information Interchange)码是 比较早的编码形式,总共定义128个字符,用了一个字节的7位,也就是从 0000 00000111 1111

  • 0-32, 127:(共34个)不能直接显示字符的(控制或者通信使用的),32是空格也算在其中,127是删除符;
  • 33-64,91-96,123-126:(共21*2=42个)键盘上除了英文字母和不直接显示字符的键有21个,每一个键上有两个字符;
  • 65-90,97-122:(26*2=56个)英文字母;

对于英语系的人来说这128个字符已经够了,他们在电脑上接触的的字符也就这么多够了,说来也是奇特,英文字符26个就能通过组合用来表示英语所有意思,这种组合形式很适合计算机。而我们汉语就不同了,我想是不能通过笔画的组合组成所有汉字了。
据说汉语是最难学的语言之一😅,辛亏我已经会汉语了😀😝。

Unicode

128个字符对于其他语言是远远不够的,单汉字就有将近10万个(常用应该几千个),每个汉字都需要一个码点,其他语言中的字符也需要对其字符编码成码点,原本有很多编码方式,这些编码没有进行统一规定,就会有冲突,一个二进制数在不同编码方式中就有可能解释为不同的字符,这样使不同地区的人交流不便。

Unicode就是在这样条件下诞生,简单的说,Unicode就是大的映射表,把全世界所有语言符号都包含其中,每个符号的映射的码点的都不相同。 汉字大部分可以用4个16进制数表示,参看汉字Unicode。如:U+620E表示(这是我的姓,读róng,不是 ,在这里普及一下,我已经被叫成 戒某 无数次了🤦‍♀️🤦‍♀️🤦‍♀️)。

Unicode是一个庞大的字符集,一个很多字符与二进制数的唯一的一一对应集合。Unicode没有规定怎么存储,620E(0110 0010 0000 1110)至少需要2个字节,而其他字符可能需要更多字节。像U+0041表示英文字母A,如果也需要用2个字节或者更多,则前面的有一些字节都是0,那么太浪费资源了,为了减少空间的浪费就出现了UTF8。

UTF8

UTF8是Unicode存储一种实现方式。它采用变字节数(1-4个)来节约资源。 UTF-8的编码规则很简单,只有二条:

  1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于英语字母,此时UTF-8编码和ASCII码是相同的。
  2. 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围(十六进制) UTF-8编码方式 (二进制)
0000 0000 - 0000 007F 0xxxxxxx
0000 0080 - 0000 07FF 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

这样解读UTF8编码就容易区分几个字节表示一个字符了。如果一个字节的第一位是0,那么这个字节单独表示一个字符;如果一个字节的第一位是1,那么这个字节下面连续几个1,就表示当前字符占用几个字节。

以汉字为例,看看UTF8编码实现过程: 已知的Unicode是620E(0110 0010 0000 1110)。

  1. 620E 在第三行范围内(0000 0800 - 0000 FFFF),因此需要三个字节编码,即 1110xxxx 10xxxxxx 10xxxxxx
  2. 的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。得的UTF8编码的十六进制是E6888E

 参考:字符编码笔记:ASCII,Unicode和UTF-8

坚持原创技术分享,您的支持将鼓励我继续创作!
  • 本文标题: 关于字符编码
  • 本文作者: AndyRon
  • 发布时间: 2018年09月14日 - 13:38
  • 最后更新: 2018年09月16日 - 17:16
  • 本文链接: http://andyron.com/2018/ascii-unicode-utf8.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!