Чтение двоичного файла с помощью 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, прямые или последовательные файлы? Они разные и несовместимые.
Владимир Ф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.