6.4.5 二进制文件与随机存取*

优质
小牛编辑
130浏览
2023-12-01

6.4.5 二进制文件与随机存取*

前面介绍的文件处理是针对文本文件的,并且主要是顺序存取文件。本节简单介绍二进 制文件的处理以及文件的随机存取。

二进制文件

任何文件在底层都是字节序列。文本文件的字节可解释成字符的编码:如果是 ASCII 编码,则每个字节表示一个字符;如果是 GBK 编码,则每两个字节表示一个汉字。对文本 文件的处理完全基于这种字符解释。而二进制文件的字节序列表示任意的二进制数据,不能 解释为字符序列。对二进制文件的处理也必须基于特定的解释来进行。

Python 语言支持对二进制文件的处理,处理过程仍然是“打开-读写-关闭”三部曲。 打开二进制文件时必须指明“以二进制方式打开”,具体就是用"rb"、"wb"和"ab"分别表

示读打开、写打开和追加打开。例如:

>>> bf1 = open("c:/windows/notepad.exe","rb")
>>> bf1.read(10)
'MZ\x90\x00\x03\x00\x00\x00\x04\x00'
>>> bf2 = open("c:/windows/explorer.exe","rb")
>>> bf2.read(10)
'MZ\x90\x00\x03\x00\x00\x00\x04\x00'

这里我们分别打开了两个常用的 Windows 应用程序文件:记事本和资源管理器,并且各读 了头 10 个字节的内容。从输出结果可见,这些字节一般不能解释成字符①。细心的读者还可 以发现,notepad.exe 和 explorer.exe 这两个文件的头 10 个字符是一样的。这一点都不奇怪, 因为它们都是 exe 文件,而 exe 文件是有规定的文件头格式的。作为练习,读者不妨以二进 制方式打开几个.jpg 文件,并读取文件头若干字节的数据,看看有什么发现。

当然我们还可以将二进制文件以"wb"和"ab"方式打开,从而可以修改二进制文件。不过 除非你知道自己在做什么,一般不要尝试修改二进制文件,因为可能破坏文件格式。

关闭二进制文件和关闭文本文件是一样的,调用文件对象的 close 方法即可。

文件的随机存取

文件一般都是顺序读写的,即从文件开始处按顺序读写文件内容直至文件尾。然而,有时候也需要对文件进行随机读写,即直接定位到文件的特定位置进行读写,不需要读写从文 件头到目标位置之间的内容。以读书作类比,顺序读写就像从第一页逐词逐行读到最后一页 一样,而随机读写则像跳跃式读书,略过中间所有内容直接翻到某一页。

我们说过,读写文件时可以想象有一个“读写头”,就像磁带录音机的磁头一样,当前 读写头所在位置决定了读写的内容是什么。刚打开文件时,读写头位于文件开始处;随着读 写语句的执行,读写头不断移动。顺序读写就像磁带录放机在进行正常的回放或录音,而随 机读写就像快进和快倒。

Python 文件对象提供的 seek()方法可用于文件的随机存取,其用法形如

<文件对象>.seek(n)
<文件对象>.seek(n,m)

其中,seek(n)的含义是将文件当前位置移到偏移为 n 的地方,这里的偏移是相对于文件开始位置的,即文件的第 1 个字节偏移为 0,第 2 个字节偏移为 1,依此类推。seek(n,m)的含义 是将文件当前位置移到偏移为 n 的地方,这里的偏移要依 m 值来定:m 为 0 时相对于文件 开始位置(即与 seek(n)相同),m 为 1 时相对于文件当前位置,m 为 2 时相对于文件末尾。 偏移为正数表示朝文件尾方向移动,偏移为负数表示向文件头方向移动。

① 二进制文件中也可以含有字符数据,例如 exe 文件的头两个字节是字母 MZ,这是 exe 文件的标志。

下面的语句序列首先创建一个汉字文本文件 ccfile.txt,其中每个汉字(包括标点符号) 占用 2 字节。其次,以读方式打开 ccfile.txt,然后文件当前位置移到偏移 12 处(即略过前 5 个汉字和 1 个逗号)并读取 4 个字节(即“处处”);然后倒退 16 个字节并读取 2 个字节(即“春”);最后向前移动 26 个字节并读 2 个字节(即“风”),最后显示三次读的内容所联接 而成的字符串“处处春风”。

>>> f = open("ccfile.txt","w")
>>> f.write("春眠不觉晓,处处闻啼鸟。夜来风雨声,花落知多少。")
>>> f.close()
>>> f = open("ccfile.txt","r")
>>> f.seek(12)
>>> s = f.read(4)
>>> f.seek(-16,1)
>>> s = s + f.read(2)
>>> f.seek(26,1)
>>> s = s + f.read(2)
>>> print s
处处春风
>>> f.tell()
30L

顺便说一下,文件对象还提供 tell()方法,用于确定当前读写位置。具体用法见上面演 示的最后两行,显然读完“风”后,读写头即停留在 30 号字节处。