在Python中读取二进制文件

4 投票
6 回答
20343 浏览
提问于 2025-04-15 19:21

我写了一个Python脚本,用来创建一个包含整数的二进制文件。

import struct  
pos = [7623, 3015, 3231, 3829]  
inh = open('test.bin', 'wb')  
for e in pos:  
    inh.write(struct.pack('i', e))  
inh.close()

这个脚本运行得很好,然后我尝试用下面的代码来读取'test.bin'文件。

import struct  
inh = open('test.bin', 'rb')  
for rec in inh:  
    pos = struct.unpack('i', rec)  
    print pos  
inh.close()

但是它出错了,显示了一个错误信息:

Traceback (most recent call last):   
   File "readbinary.py", line 10, in <module>  
   pos = struct.unpack('i', rec)  
   File "/usr/lib/python2.5/struct.py", line 87, in unpack  
   return o.unpack(s)  
struct.error: unpack requires a string argument of length 4

我想知道怎么用struct.unpack来读取这个文件。
非常感谢, Vipin

6 个回答

1

检查打包整数的大小:

>>> pos
[7623, 3015, 3231, 3829]
>>> [struct.pack('i',e) for e in pos]
['\xc7\x1d\x00\x00', '\xc7\x0b\x00\x00', '\x9f\x0c\x00\x00', '\xf5\x0e\x00\x00']

我们看到是4字节的字符串,这意味着读取时应该一次读取4个字节:

>>> inh=open('test.bin','rb')
>>> b1=inh.read(4)
>>> b1
'\xc7\x1d\x00\x00'
>>> struct.unpack('i',b1)
(7623,)
>>> 

这就是原始的整数!将其扩展到一个读取循环的部分留给你自己去练习。

5

我觉得“for rec in inh”应该是用来读取'行'而不是'字节'。你想要的是:

while True:
    rec = inh.read(4) # Or inh.read(struct.calcsize('i'))
    if len(rec) != 4:
        break
    (pos,) = struct.unpack('i', rec)
    print pos

或者正如其他人提到的:

while True:
    try:
        (pos,) = struct.unpack_from('i', inh)
    except (some_exception...):
        break
8

for rec in inh: 这种写法是一次读取一行数据,这对于处理二进制文件来说并不合适。你应该每次读取4个字节(可以用一个while循环和inh.read(4)来实现),或者一次性把所有数据都读到内存里,使用.read()这个方法,然后再把每4个字节分开处理。第二种方法是最简单、最实用的,只要数据量不是特别大:

import struct
with open('test.bin', 'rb') as inh:
    indata = inh.read()
for i in range(0, len(data), 4):
    pos = struct.unpack('i', data[i:i+4])  
    print(pos)  

如果你担心数据量会非常大(可能会超过你的内存),那么可以使用一个简单的生成器,这样的做法也很优雅:

import struct
def by4(f):
    rec = 'x'  # placeholder for the `while`
    while rec:
        rec = f.read(4)
        if rec: yield rec           
with open('test.bin', 'rb') as inh:
    for rec in by4(inh):
        pos = struct.unpack('i', rec)  
        print(pos)  

第二种方法的一个主要优点是,by4这个生成器可以很容易地调整(同时保持规范:每次返回一个二进制文件的4个字节数据),可以使用不同的缓冲策略,甚至可以回到第一种方法(先读取所有数据再分开处理),这可以被看作是“无限缓冲”,并且可以这样编写:

def by4(f):
    data = inf.read()
    for i in range(0, len(data), 4):
        yield data[i:i+4]

这样做的同时,"应用逻辑"(也就是如何处理这些4字节的数据块)保持不变,并且与输入输出层是独立的(输入输出的部分被封装在生成器里面)。

撰写回答