Python-CookBook:39、在字节串上执行文本操作

本贴最后更新于 1695 天前,其中的信息可能已经时移世改

 问题

我们想在字节串(Byte String)上执行常见的文本操作(例如,拆分、搜索和替换)。

 解决方案

字节串已经支持大多数和文本字符串一样的内建操作。例如:

>>> data = b'Hello World'
>>> data[0:5]
b'Hello'
>>> data.startswith(b'Hello')
True
>>> data.split()
[b'Hello', b'World']
>>> data.replace(b'Hello', b'Hello Cruel')
b'Hello Cruel World'
>>>

类似这样的操作在字节数组上也能完成。例如:

>>> data = bytearray(b'Hello World')
>>> data[0:5]
bytearray(b'Hello')
>>> data.startswith(b'Hello')
True
>>> data.split()
[bytearray(b'Hello'), bytearray(b'World')]
>>> data.replace(b'Hello', b'Hello Cruel')
bytearray(b'Hello Cruel World')
>>>

我们可以在字节串上执行正则表达式的模式匹配操作,但是模式本身需要以字节串的形式来指定。示例如下:

>>>
>>> data = b'FOO:BAR,SPAM'
>>> import re
>>> re.split('[:,]',data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.3/re.py", line 191, in split
    return _compile(pattern, flags).split(string, maxsplit)
TypeError: can't use a string pattern on a bytes-like object

>>> re.split(b'[:,]',data)  # Notice: pattern as bytes
[b'FOO', b'BAR', b'SPAM']
>>>

2.20.3 讨论

就绝大部分情况而言,几乎所有能在文本字符串上执行的操作同样也可以在字节串上进行。但是,还是有几个显著的区别值得大家注意。例如:

>>> a = 'Hello World'       # Text string
>>> a[0]
'H'
>>> a[1]
'e'
>>> b = b'Hello World'     # Byte string
>>> b[0]
72
>>> b[1]
101
>>>

这种语义上的差异会对试图按照字符的方式处理面向字节流数据的程序带来影响。

其次,字节串并没有提供一个漂亮的字符串表示,因此打印结果并不干净利落,除非首先将其解码为文本字符串。示例如下:

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

同样道理,在字节串上是没有普通字符串那样的格式化操作的。

>>> b'%10s %10d %10.2f' % (b'ACME', 100, 490.1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for %: 'bytes' and 'tuple'

>>> b'{} {} {}'.format(b'ACME', 100, 490.1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'bytes' object has no attribute 'format'
>>>

如果想在字节串上做任何形式的格式化操作,应该使用普通的文本字符串然后再做编码。示例如下:

>>> '{:10s} {:10d} {:10.2f}'.format('ACME', 100, 490.1).encode('ascii')
b'ACME                  100      490.10'
>>>

最后,需要注意的是使用字节串会改变某些特定操作的语义——尤其是那些与文件系统相关的操作。例如,如果提供一个以字节而不是文本字符串来编码的文件名,文件系统通常都会禁止对文件名的编码/解码。示例如下:

>>> # Write a UTF-8 filename
>>> with open('jalape\xf1o.txt', 'w') as f:
...      f.write('spicy')
...

>>> # Get a directory listing
>>> import os
>>> os.listdir('.')            # Text string (names are decoded)
['Jalapeño.txt']
>>> os.listdir(b'.')          # Byte string (names left as bytes)
[b'jalapen\xcc\x83o.txt']
>>>

请注意这个例子中的最后部分,本例中以字节串作为目录名从而导致产生的名称以未经编码的原始字节形式返回。在显示目录内容时,文件名包含了原始的 UTF-8 编码。有关文件名的处理请参阅 5.15 节。

最后要说的是,有些程序员可能会因为性能上有可能得到提升而倾向于将字节串作为文本字符串的替代来使用。尽管操纵字节确实要比文本来的略微高效一些(由于同 Unicode 相关的固有开销较高),但这么做通常会导致非常混乱和不符合语言习惯的代码。我们常会发现字节串和 Python 中许多其他部分并不能很好地相容,这样为了保证结果的正确性,我们只能手动去执行各种各样的编码/解码操作。坦白地说,如果要同文本打交道,在程序中使用普通的文本字符串就好,不要用字节串。

  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    535 引用 • 672 回帖
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    76 引用 • 390 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...