Python学习(二)

158 阅读10分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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编码
A0100000100000000 0100000101000001
01001110 0010110111100100 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的编码格式打开文件,这样就不会出现乱码了。

字符串格式化