Python|学习笔记(三)- 字符编码

Python学习笔记系列

Posted by Jack on January 4, 2015

前言

本篇是Python学习笔记系列的第(三)篇,主要介绍了字符编码相关的知识。

字符编码

计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。

最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大整数就是255(2^8 - 1,二进制11111111 = 十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535(2^16 - 1),4个字节可以表示的最大整数是4294967295(2^32 - 1)。

ASCII编码

由于计算机是美国人发明的,所以最早只有127个字符被编码到计算机,包含了英文字母、数字和一些符号,这些字符用一个字节就足够表示,这个编码被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。

GB2312

但是中文不比英文字母,总数庞大,一个字节明显不够,至少需要两个字节,而且还不能和ASSCII编码冲突,所以中国制订了GB2312编码,将汉字映射到数字,从而编码进计算机。

Unicode

然而世界上语言成百上千,各有各的标准,就不可避免会产生冲突,结果就是在多语言混合的文本中,会出现乱码的问题。因此Unicode编码应运而生。Unicode把所有语言都统一到了一套编码中,这样就不会出现乱码问题了。现代的操作系统和大多数编程语言都直接支持Unicode。

  • 区别

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。

UTF-8

如果统一成Unicode,乱码的问题是消失了,但是当文本全是英文的时候,使用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输方面都十分不划算。为了解决这个问题,又出现了UTF-8的编码,UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果需要存储或传输的文本包含大量的英文字符,用UTF-8编码就能节省空间和流量。

  • 计算机系统编码工作方式

搞清楚了ASCII、Unicode和UTF-8的关系,我们来讲一下计算机系统通用的字符编码工作方式:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者进行网络传输时,就转化为UFT-8编码

Python字符串

搞清楚字符编码之后,我们就来说说Python的字符串。在Python3中,字符串是以Unicode编码的,所以Python支持多语言。

字符与数字之间的转换

对单个字符,通过 ord() 函数可以获取字符的整数表示,通过 chr() 函数可以把编码转化为对应的字符:

>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'
字符串类型(str)与字节类型 (bytes)

Python的字符串类型是 str,在内存中以Unicode表示,一个字符对应若干个字节。如果要保存到磁盘或者在网络上传输,就需要把 str 变为以字节为单位的 bytes。Python对 bytes 类型的数据用带 b 前缀的单引号或双引号表示:x = b'ABC'

注意区分 ABCb'ABC',前者是字符串,后者虽然显示内容和前者一样,但 bytes 的每个字符都只占用一个字节。

以Unicode表示的字符串通过 encode() 方法可以编码为指定的 bytes,例如:

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'

从网络或者硬盘读取的字节流,读到的是 bytes ,通过 decode() 方法解码成字符串:

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

在操作字符串时,我们经常遇到字符串和 bytes 的互相转换。为了避免乱码问题,应当始终坚持使用 UTF-8 编码对字符串和 bytes 进行转换。

如果bytes中包含无法解码的字节,decode() 方法会报错,如果bytes中只有一小部分无效的字节,可以传入 errors='ignore' 忽略错误的字节:

>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

Python文件编码

由于Python源代码也是一个文本文件,当代码中包含中文时,在保存文件时,务必指定保存为UTF-8编码。同时,为了让Python解释器读取源码时按照UTF-8编码读取,通常在文件开头写上这两行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

注:1. 第一行是为了告诉Linux/ MacOS系统,这是一个Python可执行程序,Windows系统会忽略这个注释;2. 第二行注释是为了告诉Python解释器,按照UTF-8编码读取源码。申明了UTF-8编码并不意味着你的 .py 文件就是UTF-8编码的,必须要确保文本编辑器正在使用 UTF-8 without BOM 编码。