Чтение двоичного файла с помощью Python

105

Мне особенно сложно читать двоичный файл с помощью Python. Можешь дать мне руку? Мне нужно прочитать этот файл, который в Fortran 90 легко читается

int*4 n_particles, n_groups
real*4 group_id(n_particles)
read (*) n_particles, n_groups
read (*) (group_id(j),j=1,n_particles)

Подробно формат файла:

Bytes 1-4 -- The integer 8.
Bytes 5-8 -- The number of particles, N.
Bytes 9-12 -- The number of groups.
Bytes 13-16 -- The integer 8.
Bytes 17-20 -- The integer 4*N.
Next many bytes -- The group ID numbers for all the particles.
Last 4 bytes -- The integer 4*N. 

Как я могу прочитать это с помощью Python? Я перепробовал все, но ничего не вышло. Есть ли шанс, что я могу использовать программу f90 на Python, прочитать этот двоичный файл, а затем сохранить данные, которые мне нужны?

Брайан
источник
1
Был ли этот файл написан программой на Фортране? Если да, то как это было написано, поскольку Fortran по умолчанию добавляет дополнительные данные перед каждой записью, которую он записывает в файл. Возможно, вам придется позаботиться об этом при чтении данных.
Крис,
1
Пожалуйста, проигнорируйте мой предыдущий комментарий, промежуточные числа 8 и 4 * N явно являются дополнительными данными.
Крис
2
Также см. Ответы на вопрос о чтении двоичного файла в python .
Крис
Функция Numpy fromfileпозволяет легко читать двоичные файлы. Я рекомендую это.
littleO
... и всегда следите за порядком байтов, особенно. при портировании между компьютерами разных производителей.
DragonLord

Ответы:

157

Прочтите содержимое двоичного файла следующим образом:

with open(fileName, mode='rb') as file: # b is important -> binary
    fileContent = file.read()

затем «распаковать» двоичные данные с помощью struct.unpack :

Начальные байты: struct.unpack("iiiii", fileContent[:20])

Тело: игнорировать байты заголовка и завершающий байт (= 24); Оставшаяся часть образует тело, чтобы узнать количество байтов в теле, сделайте целочисленное деление на 4; Полученное частное умножается на строку, 'i'чтобы создать правильный формат для метода распаковки:

struct.unpack("i" * ((len(fileContent) -24) // 4), fileContent[20:-4])

Конечный байт: struct.unpack("i", fileContent[-4:])

гекко
источник
Не могли бы вы взглянуть на этот другой пост? stackoverflow.com/questions/8092469/… ... Я снова должен прочитать другой двоичный файл, но в этом случае я не знаю подробно структуру байта. Например, я выяснил, что иногда встречается целое число 8. Однако с IDL эти данные действительно просто читать. Могу ли я сделать то же самое с Python?
Брайан
Пожалуйста, укажите (в другом сообщении, а не здесь), почему вас не устраивают опубликованные ответы и комментарии. Возможно, вам также следует обновить вопрос, чтобы предоставить более подробную информацию ... Я посмотрю на него, когда он будет обновлен.
gecco 04
См. Этот ответ, если вам нужно преобразовать распакованный char [] в строку.
PeterM
import struct
JW
23

В общем, я бы порекомендовал вам изучить возможность использования для этого модуля Python struct . Это стандартно для Python, и вам должно быть легко перевести спецификацию вашего вопроса в строку форматирования, подходящую для struct.unpack().

Обратите внимание, что если между полями и вокруг них есть «невидимые» отступы, вам нужно будет это выяснить и включить в unpack()вызов, иначе вы прочитаете неправильные биты.

Прочитать содержимое файла, чтобы было что распаковать, довольно тривиально:

import struct

data = open("from_fortran.bin", "rb").read()

(eight, N) = struct.unpack("@II", data)

Это распаковывает первые два поля, предполагая, что они начинаются в самом начале файла (без заполнения или посторонних данных), а также в предположении собственного порядка байтов ( @символа). Символ Is в строке форматирования означает «целое число без знака, 32 бита».

размотать
источник
хорошо, но я даже не знаю, как читать байты файла. Из моего вопроса, как я могу прочитать файл с 5 по 8 байтов, а затем преобразовать результат в целое число? Извините, но я новичок в Python.
Брайан
14

Вы можете использовать numpy.fromfile, который может читать данные как из текстовых, так и из двоичных файлов. Сначала вы должны создать тип данных, представляющий ваш формат файла, используя numpy.dtype, а затем прочитать этот тип из файла, используя numpy.fromfile.

Крис
источник
2
Это легко упустить! Документы немного тонкие; см. reddit.com/r/Python/comments/19q8nt/… для некоторых обсуждений
потеряно
11

Чтобы прочитать двоичный файл в bytesобъект:

from pathlib import Path
data = Path('/path/to/file').read_bytes()  # Python 3.5+

Чтобы создать intиз байтов 0-3 данных:

i = int.from_bytes(data[:4], byteorder='little', signed=False)

Чтобы распаковать несколько ints из данных:

import struct
ints = struct.unpack('iiii', data[:16])
Евгений Ярмаш
источник
0

Я тоже обнаружил, что Python не хватает чтения и записи двоичных файлов, поэтому я написал небольшой модуль (для Python 3.6+).

С двоичным файлом вы бы сделали что-то вроде этого (я предполагаю, так как я не знаю Фортран):

import binaryfile

def particle_file(f):
    f.array('group_ids')  # Declare group_ids to be an array (so we can use it in a loop)
    f.skip(4)  # Bytes 1-4
    num_particles = f.count('num_particles', 'group_ids', 4)  # Bytes 5-8
    f.int('num_groups', 4)  # Bytes 9-12
    f.skip(8)  # Bytes 13-20
    for i in range(num_particles):
        f.struct('group_ids', '>f')  # 4 bytes x num_particles
    f.skip(4)

with open('myfile.bin', 'rb') as fh:
    result = binaryfile.read(fh, particle_file)
print(result)

Что дает такой вывод:

{
    'group_ids': [(1.0,), (0.0,), (2.0,), (0.0,), (1.0,)],
    '__skipped': [b'\x00\x00\x00\x08', b'\x00\x00\x00\x08\x00\x00\x00\x14', b'\x00\x00\x00\x14'],
    'num_particles': 5,
    'num_groups': 3
}

Я использовал skip (), чтобы пропустить дополнительные данные, добавляемые Fortran, но вместо этого вы можете добавить утилиту для правильной обработки записей Fortran. Если вы это сделаете, запрос на перенос будет приветствоваться.

Факс
источник
-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()
Ишитри
источник
7
Вероятно, стоит немного пояснить, почему это лучше (или, по крайней мере, не хуже) других ответов.
Фил
2
Вы тестировали и проверяли, что это работает с двоичным файлом, созданным fortran?
agentp
1
А также объясните, что он делает ... Что такое рассол? Что pickle.loadзагружает? Загружает ли он поток Fortran, прямые или последовательные файлы? Они разные и несовместимые.
Владимир Ф