Чтение двоичного файла в 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 .
Большое спасибо заранее, Випин

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(f): pos = struct.unpack('i', rec) print pos 

Ключевым преимуществом этого второго подхода является то, что генератор by4 можно легко by4 (при сохранении спецификаций: вернуть данные двоичного файла по 4 байта за раз), чтобы использовать другую стратегию реализации для буферизации, вплоть до первого подхода ( прочитайте все, а затем выложите его), который можно увидеть как «бесконечную буферизацию» и закодированную:

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

оставляя «логику приложения» (что делать с этим потоком из 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 

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

 >>> 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! Переход в цикл чтения остается в виде упражнения.

Вы также можете использовать 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 чтобы прочитать его.

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

 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' 

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