一、为什么会有字符串编码问题?
最开始的计算机只针对数字和英文字母进行编码,也就是ASCII码,但是随着计算机的普及,各种语言都需要编码,因此出现了一系列的标准,比如中国制定了GB2312,日本把日文编到Shift_JIS
里,韩国把韩文编到Euc-kr
里。所以各种编码混在一起就会出现乱码。
Unicode编码应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。
也就是说一个汉字我们用两个字节16bytes来表示,一个英文字母也是如此,比如A就是00000000 01000001。这样一来,会出现很大的空间浪费,因此UTF-8出现了。
UTF-8采取变长编码的方式,也就是说针对不同的字符采取不同的编码方式,例如英文字母用一个字节进行编码,一个汉字用三个字节来进行编码。
如下可以看出几种编码的区别:
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | x | 01001110 00101101 | 11100100 10111000 10101101 |
二、计算机中字符串的编码方式
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
也就是说在内存使用时,都用Unicode进行编码,当存储或者传输时,使用UTF-8编码来减少存储空间。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
三、Python中的字符串
python3中,字符串是以Unicode编码的,支持多种语言。
对于单个字符的编码,Python提供了ord()
函数获取字符的整数表示,chr()
函数把编码转换为对应的字符:
>>> ord('A') 65 >>> ord('中') 20013 >>> chr(66) 'B' >>> chr(25991) '文'
由于Python的字符串类型是str
,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str
变为以字节为单位的bytes
。
Python对bytes
类型的数据用带b
前缀的单引号或双引号表示:
x = b'ABC'
'ABC'与b'ABC'之间是有区别的,两者表示的内容相同,但是占用的字节不同,前者占用6个字节,后者占用3个。
以Unicode表示的str
通过encode()
方法可以编码为指定的bytes
,例如:
>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'xe4xb8xadxe6x96x87' >>> '中文'.encode('ascii') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
可以看到,有些无法转化的格式会提示报错
反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes
。要把bytes
变为str
,就需要用decode()
方法:
>>> b'ABC'.decode('ascii') 'ABC' >>> b'xe4xb8xadxe6x96x87'.decode('utf-8') '中文'
如果bytes
中包含无法解码的字节,decode()
方法会报错:
>>> b'xe4xb8xadxff'.decode('utf-8') Traceback (most recent call last): ... UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte
如果bytes
中只有一小部分无效的字节,可以传入errors='ignore'
忽略错误的字节:
>>> b'xe4xb8xadxff'.decode('utf-8', errors='ignore') '中'
由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这行:
# -*- coding: utf-8 -*-
申明了UTF-8编码并不意味着你的.py
文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码: