Чтение двоичного файла в Python - PullRequest
2 голосов
/ 16 февраля 2010

Я написал скрипт на 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 ]

8 голосов
/ 16 февраля 2010

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-байтовых блоков) нетронутой и независимой от уровня ввода-вывода (который инкапсулируется в генераторе).

5 голосов
/ 16 февраля 2010

Я думаю, что "для записи в 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
1 голос
/ 17 марта 2011

Эта функция читает все байты из файла

def read_binary_file(filename):
try:
    f = open(filename, 'rb')
    n = os.path.getsize(filename)
    data = array.array('B')
    data.read(f, n)
    f.close()
    fsize = data.__len__()
    return (fsize, data)

except IOError:
    return (-1, [])

# somewhere in your code
t = read_binary_file(FILENAME)
fsize = t[0]

if (fsize > 0):
    data = t[1]
    # work with data
else:
    print 'Error reading file'
1 голос
/ 16 февраля 2010

Вы также можете использовать array, если хотите:

import array  
pos = array.array('i', [7623, 3015, 3231, 3829]) 
inh = open('test.bin', 'wb')  
pos.write(inh)
inh.close()

Затем используйте array.array.fromfile или fromstring, чтобы прочитать его назад.

1 голос
/ 16 февраля 2010

Проверьте размер упакованных целых чисел:

>>> 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,)
>>> 

Это оригинальный int! Расширение в цикл чтения оставлено как упражнение.

0 голосов
/ 16 февраля 2010

Ваш итератор не читает 4 байта за раз, поэтому я думаю, что это довольно запутанно. Как упоминалось в SilentGhost, лучше всего использовать unpack_from ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...