python编码

字符编码

常用字符编码有ASCII,GB2312,Unicode,UTF-8等,为什么需要这么多不同的编码,又是怎么来的呢?
计算机能够识别的只有二进制的0和1,要处理字母等类型只能转换为类似01010001的二进制数字才能处理;人要能够正常阅读,也需要计算机将二进制数转换为对应的字母。那么如何转换,转换的规则和标准又是什么?这就是我们常见的编码所规定的。

ASCII

计算机是美国人发明的,因此最早只有英语中的127个字符(包括大小写字母、数字、特殊符号等)被编码到计算机里,这个编码表就是ASCII编码。根据前面一个字节(8比特)最多可以表示256个字符,那么对于英文中的100多个字符使用一个字节中的前7位就可以表示。

GBK

英文是可以被计算机识别了,那么中文怎么破?为了解决汉字问题,中国国家标准总局提出了GB2312编码,收录了6763个汉字,后来又在此基础上创建了GBK编码,收录了27484汉字,同时收录了包括藏文、蒙文等在内的主要少数名族文字。

Unicode

中文使用GBK编码,那么对于其他国家的文字如何处理,各个国家都有建立了自己的标准。为了统一标准,统一联盟国际组织提出了Unicode编码,该编码将所有语言统一到一套编码。
Unicode标准也在不断发展,最常见的是两个字节表示一个字符(生僻字符可能需要4个字节)。
ASCIIUnicode主要区别:ASCII编码使用1个字节,Unicode编码通常是2个字节。

字母A用ASCII编码是十进制的65,二进制的01000001;
字符0用ASCII编码是十进制的48,二进制的00110000;
汉字已经超出ASCII编码范围,用Unicode编码是十进制的20013,二进制的01001110 00101101;
ASCII编码的A用Unicode编码,只要在前面补0就可以,A的Unicode编码是00000000 01000001

从上面可以看出,假如对英文使用Unicode编码要比ASCII编码多一倍的存储空间,在存储和传输上不方便。
所以本着节约的精神,又出现了把Unicode编码转换为可变长编码的UTF-8编码。UTF-8编码把一个Unicode字符根据不同数字大小编码成1-6个字节,常用的英文字母编码成1个字节,汉字通常是3个字节。当传输的文本中包含大量的英文字符时,用UTF-8编码可以节省空间。
上面内容总结一下就是

  • 为处理英文字符,出现了ASCII码。
  • 为处理中文字符,出现了GB2312GBK
  • 为统一处理不同国家不同语言,出现了Unicode编码。
  • 为提高Unicode传输和存储的性能,出现了UTF-8,它是Unicode的一种实现方式。

    python2的字符编码

    python2中默认的字符编码是ASCII码,也就是说在处理数据时,若没有指定它的编码类型,默认将会当做ASCII码来处理。当编写的python文件中包含有中文字符时就会报错。
    1
    2
    3
    #!/usr/bin/env python
    s = "是否乱码"
    print(s)

执行结果
python-code-default
根据上述结果可以看到出错原因是python将整个脚本当做ASCII码处理,但是出现的中文是否乱码ASCII码无法处理。处理方式很简单,在头部添加一行编码声明。

1
2
3
4
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
s = "是否乱码"
print(s)

执行结果
pycharm执行
python-code-utf8-pycharm
windows命令行执行
python-code-utf8-cmd
声明编码方式之后发现在pycahrm执行输出正确信息,但是在windows命令行输出乱码。
windows命令行默认使用的是GBK编码,但是在python脚本中使用的是UTF-8,两边不一致导致出现乱码,只要修改两者一致即可。

1
2
3
4
#!/usr/bin/env python
# -*- coding: GBK -*-
s = "是否乱码"
print(s)

执行结果
python-code-gbk
此时可以看到在命令行执行结果显示正确,但是相应的在pycharm执行就会出现乱码的情况。也进一步说明导致乱码就是编码格式不一致。也就是说,当需要操作系统正确输出一个字符时,除了要知道该字符的字符编码,还需要知道自己使用系统的字符编码,两者一致时就不会出现所谓乱码

decode()和encode()

decode()方法将其他编码字符转换成Unicode编码字符。
encode()方法将Unicode编码字符转换成其他编码字符。
python-code-encode
上述命令在pycharm自带的终端执行,其默认字符编码为UTF-8
直接输入s出现的'\xe6\x98\xaf\xe5\x90\xa6\xe4\xb9\xb1\xe7\xa0\x81'为对应的UTF-8字符串。
使用decode()方法将s转换为unicode编码,此时输入unicode_s出现的u'\u662f\u5426\u4e71\u7801'为unicode字符串。
使用encode()方法将unicode_s转换为GB2312编码,此时输入gb2312_s出现的'\xca\xc7\xb7\xf1\xc2\xd2\xc2\xeb'为gb2312字符串。由于终端为UTF-8编码,所以使用print gb2312_s会出现乱码情况。
在windows终端可以正常输出gb2312编码的字符串。
python-code-decode
总结

  • python2中可以直接查看unicode字符串。
  • python2中对于字符编码的转换通过unicode作为中间人进行转换。
  • decode()方法与在字符串前加u的方法实现的效果相同。
    python-code-u

    python2中的列表

    1
    2
    3
    4
    5
    >>> list1 = ["哈哈","ABC"]
    >>> list1
    ['\xb9\xfe\xb9\xfe', 'ABC']
    >>> print list1[0]
    哈哈

当一个中文的字符出现在列表(或元组或字典)中,它不会被显示为中文而是显示为字符串。但是当该字符窜从列表中取出再使用print时就可以正常显示为中文。
字符串是所有字符在python2中的本质形态,该字符串是计算机可以理解的,不是通常所说的乱码。在python3中就不存在这种问题了。

python3的字符编码

在python3中默认编码方式为UTF-8,所以coding声明可以不用写,但为了兼容python2建议添加。
python3中字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。当需要传输或者保存到硬盘时,就需要把str变为以字节为单位的bytes
以Unicode表示的str通过encode()方法可以编码为指定的bytes

1
2
3
4
>>> "ABC".encode('ASCII')
b'ABC'
>>> "是否乱码".encode('UTF-8')
b'\xe6\x98\xaf\xe5\x90\xa6\xe4\xb9\xb1\xe7\xa0\x81'

  • bytes类型的数据用带b前缀的单引号或者双引号表示。
  • bytes中无法显示为ASCII字符的字节,用\x##表示。
    相反,要将bytes变为str,就需要使用decode()方法。
    1
    2
    3
    4
    >>> b'ABC'.decode('ASCII')
    'ABC'
    >>> b'\xe6\x98\xaf\xe5\x90\xa6\xe4\xb9\xb1\xe7\xa0\x81'.decode('UTF-8')
    '是否乱码'

上面说到的编码问题主要是python2中,请注意区分版本。
即使在python3中,涉及到strbytes转换时,非特殊情况一定要使用UTF-8编码。

Recommended Posts