无论是用Python还是其它语言,处理字符编码都是一件极其痛苦的事情。我们无法忘记被UnicodeDecodeError和UnicodeEncodeError支配的恐惧。本文将以Python3为例,带你了解Unicode的基本概念,以及字符串编码解码的工作原理,让你从根本上理解字符编码。

首先,我们来看一看什么是字符编码。我们知道,计算机里所有的信息都是以数字的形式来表示的,字符也不例外。要想表示一个字符,首先要把它转换成对应的数字。

最简单的字符编码就是大家耳熟能详的ASCII码。ASCII码能够表示127个字符,它们包括:大写字母,小写字母,标点符号,空格和一些非打印字符。每一个字符都对应一个代码点(code point)。我们可以认为一个代码点就是一个整数。ASCII码中包含的字符和它们对应的代码点范围如下:

通俗的讲什么是unicode(看这一篇就够了)(1)

整个ASCII表包含128个字符。没有出在这张表里的字符无法用ASCII编码来表示。

Python的string模块里定义了完整的ASCII字符集

通俗的讲什么是unicode(看这一篇就够了)(2)

我们可以在代码中直接引用这些常量

通俗的讲什么是unicode(看这一篇就够了)(3)

ASCII编码非常简单,而且能够表示所有英文字母和常见符号。但是这样就够了吗?当然不是。如果我们想在计算机中表示一个汉字或者想要表示一个日语的假名该怎么办?很显然,只有128个字符的ASCII码是远远不够的。这就是Unicode产生的原因。

其实,Unicode和ASCII码的目的是一样的,只不过Unicode所包含的字符要远远多于ASCII码。事实上,Unicode最多可以容纳1,114,112个code point。把世界上所有语言的文字和符号加起来应该也够用了。比如说汉字“我”在Unicode中的编号是6211。为了与ASCII码兼容,Unicode规定,前128个字符与ASCII码是相同的。所以一个不大于128的code point所代表的Unicode字符与ASCII码相同,而超过128的字符则为ASCII无法表示的。

由于Unicode的字符集太过庞大,因此,它无法像ASCII码一样,用一个字节就可以表示。所以,Unicode势必要用多于一个字节来表示一个字符。这时就遇到一个问题,那就是在计算机进行读取的时候,它怎么知道那几个字节是属于一个字符的呢?比如,有一串字节数据“0xaa 0xbb 0xcc 0xdd 0xff”,我们怎么知道哪几个字节组合在一起来表示一个字符呢?那么就需要一种约定,来告诉我们如何把code point转换成字节流,或者反过来。这就是编码和解码。

既然是约定,那就意味着可以用不同的方法。事实上,最常用的编码方式是UTF-8,除此之外还有UTF-16,UTF-32等。由于篇幅有限,这里就不再展开每种编码的具体细节了。大家只需要知道,不管是什么编码,他们都是用来实现一个code point和一串字节流之间的转换的。

在Python3中,str类型可以用来存储可读的Unicode文本,而bytes则用来存储二进制数据。字符串编码和解码的过程就像这样

通俗的讲什么是unicode(看这一篇就够了)(4)

例如:

通俗的讲什么是unicode(看这一篇就够了)(5)

这里,str.encode()的输出是一个bytes对象,其中保存了Unicode字符串编码后的字节流。

这里要注意的是,要想正确地对编码数据进行解码,必须使用匹配的编解码方法,否则解码的数据就会使错误的,比如下面这个例子:

通俗的讲什么是unicode(看这一篇就够了)(6)

可以看到,我们编码的数据是一串希腊字母,但是由于解码用的方法和编码不一致,所以接出来的居然是一串韩文字符,错得有点离谱了。

总结

  1. Unicode实际上就是一个巨大的字符集,里面保存着所有人类已知的文字和字符;
  2. 每一个Unicode都对应一个code point;
  3. 编码是把code point转换成字节流的过程,而解码是把字节流转换成code point的过程;
  4. 编码的方法有很多种,最常见的是UTF-8,这也是Python3默认的编码方式;
  5. 编解码方式必须要一致,否则就会导致解码结果错误。

关注我,更多精彩文章!

,