2010-02-16 3 views
2

Я написал скрипт python для создания двоичного файла целых чисел.Чтение двоичного файла в 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

ответ

0

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

+0

@Xorlev, за исключением того, что нет поста от SilentGhost показывая здесь больше ... –

4

Я думаю, что «для 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 
7

for rec in inh: читает одну строку в то время - не то, что вы хотите для двоичного файла. Прочитайте 4 байта за один раз (с циклом while) (или прочитайте все в памяти с помощью одного вызова .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 

Если вы опасаться потенциально огромные объемы данных (которые будут занимать больше памяти, чем у вас имеется), A простой генератор предлагает элегантную альтернативу:

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

+2

Первую версия by4 будет висеть один раз «гес = f.read (4) "возвращает ноль. Нет выхода из цикла while. –

+0

@Mark, вы правы - редактируете A для исправления. –

+0

Альтернативой является «import functools; reader = functools.partial (f.read, 4); для rec in iter (reader, ''): 'construct. В противном случае, чтобы избежать functools и его ускорения: 'for rec in iter (lambda: f.read (4), '')') – tzot

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

Это оригинальный ИНТ ! Переход в цикл чтения остается в виде упражнения.

1

Вы можете, вероятно, использовать 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

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

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' 
Смежные вопросы