Python 中有两种主要的字符串类型:表示二进制数据的字节字符串(字节序列)和表示人类可读文本的 Unicode 字符串(Unicode 代码点序列)。一个转换成另一个很简单(☯):
unicode_text = bytestring.decode(character_encoding)
bytestring = unicode_text.encode(character_encoding)
如果您以二进制模式打开文件,例如'rb',则file.read() 返回一个字节串(bytes 类型):
>>> b'A' == b'\x41' == chr(0b1000001).encode()
True
有几种方法可以用来对字节进行分类:
-
字符串方法如bytes.isdigit():
>>> b'1'.isdigit()
True
-
字符串常量,例如string.printable
>>> import string
>>> b'!' in string.printable.encode()
True
-
正则表达式,例如\d
>>> import re
>>> bool(re.match(br'\d+$', b'123'))
True
-
curses.ascii 模块中的分类函数,例如 curses.ascii.isprint()
>>> from curses import ascii
>>> bytearray(filter(ascii.isprint, b'123'))
bytearray(b'123')
bytearray 是一个可变的字节序列 - 与字节串不同,您可以就地更改它,例如,将每个大写的第 3 个字节小写:
>>> import string
>>> a = bytearray(b'ABCDEF_')
>>> uppercase = string.ascii_uppercase.encode()
>>> a[::3] = [b | 0b0100000 if b in uppercase else b
... for b in a[::3]]
>>> a
bytearray(b'aBCdEF_')
注意:b'ad' 是小写但b'_' 保持不变。
要就地修改二进制文件,您可以使用 mmap 模块,例如,将 'file' 中每隔一行的第 4 列小写:
#!/usr/bin/env python3
import mmap
import string
uppercase = string.ascii_uppercase.encode()
ncolumn = 3 # select 4th column
with open('file', 'r+b') as file, \
mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_WRITE) as mm:
while True:
mm.readline() # ignore every other line
pos = mm.tell() # remember current position
if not mm.readline(): # EOF
break
if mm[pos + ncolumn] in uppercase:
mm[pos + ncolumn] |= 0b0100000 # lowercase
注意:在这种情况下,Python 2 和 3 API 有所不同。代码使用 Python 3。
输入
ABCDE1
FGHIJ
ABCDE
FGHI
输出
ABCDE1
FGHiJ
ABCDE
FGHi
注意:第 4 列在第 2 行和第 4h 行变为小写。
通常,如果您想更改文件:您从文件中读取,将修改写入临时文件,并在成功时将临时文件移动到原始文件的位置:
#!/usr/bin/env python3
import os
import string
from tempfile import NamedTemporaryFile
caesar_shift = 3
filename = 'file'
def caesar_bytes(plaintext, shift, alphabet=string.ascii_lowercase.encode()):
shifted_alphabet = alphabet[shift:] + alphabet[:shift]
return plaintext.translate(plaintext.maketrans(alphabet, shifted_alphabet))
dest_dir = os.path.dirname(filename)
chunksize = 1 << 15
with open(filename, 'rb') as file, \
NamedTemporaryFile('wb', dir=dest_dir, delete=False) as tmp_file:
while True: # encrypt
chunk = file.read(chunksize)
if not chunk: # EOF
break
tmp_file.write(caesar_bytes(chunk, caesar_shift))
os.replace(tmp_file.name, filename)
输入
abc
def
ABC
DEF
输出
def
ghi
ABC
DEF
要将输出转换回来,请设置caesar_shift = -3。