持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
字符串编码
因为计算机只能处理数字,准确来说是二进制数字0和1,如果要处理文本,就必须先把文本转换成数字才能处理。最早的计算机在设计的时候采用8个比特(bit)位作为一个字节(byte)。所以,一个字节能够表示的最大整数就是255(8个比特位每个位置上都是1,换算成十进制就是255).如果要表示更大的整数,就必须用更多的字节。
ASCCII编码
最早的时候,只有127个字符被编码到计算机中,也就是英文字母,数字和一些符号,这个编码表就被称为ASCII编码。在这个编码表中,大写字母A对应的整数就是65,小写字母z对应的整数则是122。
这个编码表可以正常显示英文字符,但是对于中文等字符则力不从心,对于中文的显示,一个字节是不够的,至少需要两个字节,因此中国制定了GB2312编码,用于将中文进行编码。
不仅是对于中文,ASCII编码对于很多文字的编码都不能支持,因此不同的国家都制定了针对当前国家文字的编码。由于编码不一样,我们在处理多语言的时候很容易出现乱码的情况,为了解决这个问题,就出现了Unicode编码。
Unicode编码
Unicode将所有的语言都统一到一套编码中,这样只要是使用Unicode编码,就不会出现乱码的情况。Unicode标准也是在不断发展,目前最常用的是ucs-16编码。用两个字节表示一个字符(如果遇到非常偏僻的字符,则使用4个字节),目前的操作系统和大多数的编程语言都支持这种编码方式。
由此我们可以了解到:ASCII编码采用一个字节,而Unicode编码通常采用两个字节,如下所示:
- 字母
A在ASCII编码中的十进制是65,换算成二进制应该是01000001 - 字符
0在ASCII编码中的十进制是48,换算成二进制就是00110000,需要注意的是字符'0'和整数0是不一样的 - 汉字
中这个字已经超过了ASCII编码的范围,而使用Unicode编码的十进制则是20013,对应的二进制则是0100111000101101。
而如果我们希望将ASCII编码中的A使用Unicode进行编码,则只需要在前面补0就可以,这样转换成的十进制就还是48,即0000000000110000.
UTF-8
使用Unicode编码解决了乱码的问题,但是又出现了一个新的问题,那就是如果我们的文本基本上全都是英文的话,使用Unicode编码将会比ASCII编码多一倍的存储空间,这样在存储和数据传输的过程中非常不划算。为了解决这个问题,又出现了把Unicode编码转化为'可变长编码'的UTF-8编码。
UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。在这种情况下,如果我们传输的文本包含大量的英文字符,用UTF-8编码就能节省大量空间,如下表所示:
| 字符 | ASCII编码 | Unicode编码 | UTF-8编码 |
|---|---|---|---|
| A | 01000001 | 00000000 01000001 | 01000001 |
| 中 | 无 | 01001110 00101101 | 11100100 10111000 10101101 |
从上面的表格可以看出,使用UTF-8编码还有一个额外的好处,就是ASCII码实际上可以被看做是UTF-8编码的一部分,所以大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。
现在计算机系统通用的字符编码方式总结如下:
- 在计算机内存中,统一使用
Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
Python中的字符串
在最新的Python3版本中,字符串是使用Unicode编码的,也就是说,Python是支持多语言的,如下所示:
str1 = "this is 张三"
print(str1)
输出如下:
> python python_str.py
this is 张三
对于单个字符的编码,可以使用ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:
inputStr = input("请输入一个字符,按回车键结束:")
inputOrd = ord(inputStr)
print(inputStr,"对应的整数为:",inputOrd)
# chr()函数可以获取整数对应的字符
inputInt = int(input("请输入一个整数,按回车键结束:"))
inputChr = chr(inputInt)
print(inputInt,"对应的字符为:",inputChr)
运行上面的程序可以得到如下的输出:
请输入一个字符,按回车键结束:找
找 对应的整数为: 25214
请输入一个整数,按回车键结束:24356
24356 对应的字符为: 弤
如果知道字符的整数编码,还可以使用16进制定义字符串,如下所示:
# 使用十六进制整数编码定义字符
str2 = "\u4e2d\u6587"
print(str2)
#运行结果如下:
#中文
字节bytes
Python中的字符串在内存中使用Unicode编码表示,一个字符对应若干个字节。但是如果需要传输或者保存到硬盘上,就需要将字符串转换成以字节为单位的bytes。
Python对bytes类型的数据使用带b前缀的单引号或者双引号表示,如下所示:
byte1 = b"ABC"
byte2 = b'abc'
print(byte1,"XXX",byte2)
#输出如下
#b'ABC' XXX b'abc'
可以看到,对于定义为bytes类型的数据,输出的时候也会带上b前缀和单引号,方便我们进行判断。
字符串和字节的转换
使用Unicode表示的字符串可以通过encode()方法编码为指定的bytes,如下所示:
# 使用encode()方法将Unicode字符串转换为指定的编码
str3 = "ABC"
# 转换为ascii编码
byte3 = str3.encode("ascii")
print(str3,"ascii is:",byte3)
# 转换为utf-8编码
byte4 = str3.encode("utf-8")
print(str3,"utf-8 is:",byte4)
# 对中文使用上述编码转换
str4 = "我是谁"
byte6 = str4.encode("utf-8")
print(str4,"utf-8 is:",byte6)
byte5 = str4.encode("ascii")
print(str4,"ascii is:",byte5)
运行上面的程序可以得到如下输出:
ABC ascii is: b'ABC'
ABC utf-8 is: b'ABC'
我是谁 utf-8 is: b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81'
Traceback (most recent call last):
File "F:\Project\PythonProject\PythonStudy\python_str.py", line 39, in <module>
byte5 = str4.encode("ascii")
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
可以看到:对于全部是英文的字符串,可以正常转换为ASCII编码和UTF-8编码,但是对于不是全英文的字符串,转换为ASCII编码的时候便会出错,就是因为ASCII编码不包含对应的字符;但是可以正常专函为UTF-8编码。只不过转换之后不能显示为ASCII字符的字节,将会使用\x##显示。
上面学习了使用encode()方法将字符串转换为对应的编码,这对于我们保存内容或者传输内容起到了很大的作用。反之,如果我们需要将读取到的内容显示出来,由于此时读取到的不是Unicode编码,此时就需要使用decode()方法将原始的编码转换为Unicode编码,如下所示:
# 使用decode将字节转换为Unicode字符串
byte7 = b'ABC'
#以ascii编码的方式转换
str5 = byte7.decode("ascii")
print(byte7,"decode ascii is:",str5)
#以utf-8编码的方式转换
str6 = byte7.decode("utf-8")
print(byte7,"decode utf-8 is:",str6)
#对中文进行转换
byte8 = b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81'
#以utf-8编码的方式进行转换
str7 = byte8.decode("utf-8")
print(byte8,"decode utf-8 is:",str7)
# 以ascii编码的方式进行转换
str8 = byte8.decode("ascii")
print(byte8,"decode asccii is:",str8)
运行上面的程序,输出结果如下:
b'ABC' decode ascii is: ABC
b'ABC' decode utf-8 is: ABC
b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\x81' decode utf-8 is: 我是谁
Traceback (most recent call last):
File "F:\Project\PythonProject\PythonStudy\python_str.py", line 59, in <module>
str8 = byte8.decode("ascii")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
如果bytes中包含无法解码的字节,decode()方法会报错,如下所示:
byte9 = b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\xff'
str9 = byte9.decode("utf-8")
print(byte9,"decode utf-8 is:",str9)
# 上面的程序输出如下:
# UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 6-7: invalid continuation byte
如果能够接受字节中存在部分不能解码的字节,可以在decode()方法中传递另一个参数errors='ignore'来忽略这些错误:
byte9 = b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\xff'
str9 = byte9.decode("utf-8",errors="ignore")
print(byte9,"decode utf-8 is:",str9)
# 上面的程序输出如下:
# b'\xe6\x88\x91\xe6\x98\xaf\xe8\xb0\xff' decode utf-8 is: 我是
可以看到,程序没有报错,只是对无法解码的字节不做解码的操作。
计算字符串和字节的长度
可以使用len()函数来计算字符串和字节的长度,对于字符串来说,计算的是字符的个数,对于字节来说,计算的是字节数:
str10 = input("请输入一段文本,按回车键结束:")
print("你输入的文本的长度为:",len(str10))
byte10 = str10.encode("utf-8")
print("你输入的文本对应的字节长度为:",len(byte10))
# 针对上面的代码进行测试,得到如下的输出:
# 请输入一段文本,按回车键结束:ABC
# 你输入的文本的长度为: 3
# 你输入的文本对应的字节长度为: 3
# 请输入一段文本,按回车键结束:张三
# 你输入的文本的长度为: 2
# 你输入的文本对应的字节长度为: 6
# 请输入一段文本,按回车键结束:aa王二麻子
# 你输入的文本的长度为: 6
# 你输入的文本对应的字节长度为: 14
可以看到,对于在ASCII编码能够表示的字符内,字符串的长度和字节的长度是一样的,但是在ASCII编码能够表示的字符范围外,一般情况下字节数都会大于字符长度。中文中的一个字,一般会用3个字节表示。
为python源码文件指定编码格式
一般情况下,我们所写的python代码都是中文和英文都存在的,所以为了能够正确保存我们所写的代码,避免乱码问题,我们可以在python源码的开头添加注释,告知解释器我们的代码使用utf-8的编码格式。
# -*- coding: utf-8 -*-
另外需要注意的是:虽然我们声明了代码使用utf-8的格式,但是这并不意味着不会出现乱码问题,在文本编辑器中我们也要选择utf-8的编码格式打开文件,这样就不会出现乱码了。