Python进阶编程:编写更高效、优雅的Python代码
上QQ阅读APP看书,第一时间看更新

2.9 字节字符串操作

在实际工作中,我们有时需要对字节字符串执行如移除、搜索和替换等普通的文本操作。

字节字符串支持大部分和文本字符串一样的内置操作,相关代码(str_operation.py)示例如下:


test_str = b'Hello World'
print(test_str[0: 5])
print(test_str.startswith(b'Hello'))
print(test_str.split())
print(test_str.replace(b'World', b'Python'))

执行py文件,输出结果如下:


b'Hello'
True
[b'Hello', b'World']
b'Hello Python'

这些操作同样适用于字节数组,相关代码(str_operation.py)示例如下:


test_list = bytearray(test_str)
print(test_list[0: 5])
print(test_list.startswith(b'Hello'))
print(test_list.split())
print(test_list.replace(b'World', b'Python'))

执行py文件,输出结果如下:


bytearray(b'Hello')
True
[bytearray(b'Hello'), bytearray(b'World')]
bytearray(b'Hello Python')

我们可以使用正则表达式匹配字节字符串,但是正则表达式本身必须是字节串,示例如下:


data = b'FOO:BAR,SPAM'
import re
re.split('[:,]',data) # error
re.split(b'[:,]',data)

大多数情况下,在文本字符串上的操作均可用于字节字符串。不过有一些需要注意的点,首先字节字符串的索引操作返回的是整数而不是单独字符,相关代码(str_operation.py)示例如下:


a = 'Hello World'
print(f'str result:{a[0]}')
print(f'str result:{a[1]}')

b = b'Hello World'
print(f'binary result:{b[0]}')
print(f'binary result:{b[1]}')

执行py文件,输出结果:


str result:H
str result:e
binary result:72
binary result:101

这种语义上的区别对处理面向字节的字符数据有影响。

其次,字节字符串不会提供一个美观的字符串表示,也不能很好地打印出来,除非它们先被解码为一个文本字符串,示例如下:


s = b'Hello World'
print(s)
print(s.decode('ascii'))

类似地,字节字符串也不存在任何适用于字节字符串的格式化操作:


print(b'{} {} {}'.format(b'ACME', 100, 490.1))
print(f"{b'ACME'} {100} {200.5}'")

如想格式化字节字符串,得先使用标准的文本字符串,然后将其编码为字节字符串,示例如下:


print(f"{'ACME':10s} {100:10d} {200.5:10.2f}'".encode('ascii'))

最后需要注意的是,使用字节字符串可能会改变一些操作的语义,特别是那些与文件系统有关的操作。如使用一个编码为字节的文件名,而不是一个普通的文本字符串,会禁用文件名的编码/解码,示例(str_operation.py)如下:


with open('jalape\xf1o.txt', 'w') as f:
    f.write('spicy')

import os
os.listdir('.')
os.listdir(b'.')

注意例子中的最后部分给目录名传递一个字节字符串是怎样导致结果中文件名以未解码字节返回的。在目录中的文件名包含原始的UTF-8编码。

最后提一点,一些程序员为了提升程序执行的速度会倾向于使用字节字符串而不是文本字符串。

尽管操作字节字符串确实会比文本更加高效(因为处理文本固有的Unicode相关开销),但这样做通常会导致代码非常杂乱。在应用中,我们经常会发现字节字符串并不能和Python的其他部分工作得很好,并且还得手动处理所有的编码/解码操作。

如果要处理的是文本,我们可直接在程序中使用普通的文本字符串而不是字节字符串。