字符串和编码

一、为什么会有字符串编码问题?

  最开始的计算机只针对数字和英文字母进行编码,也就是ASCII码,但是随着计算机的普及,各种语言都需要编码,因此出现了一系列的标准,比如中国制定了GB2312,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里。所以各种编码混在一起就会出现乱码。

  Unicode编码应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

  Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。 

  也就是说一个汉字我们用两个字节16bytes来表示,一个英文字母也是如此,比如A就是00000000 01000001。这样一来,会出现很大的空间浪费,因此UTF-8出现了。

  UTF-8采取变长编码的方式,也就是说针对不同的字符采取不同的编码方式,例如英文字母用一个字节进行编码,一个汉字用三个字节来进行编码。

  如下可以看出几种编码的区别:

字符ASCIIUnicodeUTF-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编码:

 

原文地址:https://www.cnblogs.com/flightless/p/9886057.html