LINUX.ORG.RU

Считать массив из бинарного файла

 ,


1

1

Есть код на фортране, он создает бинарный файл, в котором содержится двумерный массив из real значений.

Надо считать этот файл питоном и загрузить этот массив в память программы на питоне. А лучше каждый элемент массива последовательно загружать и обрабатывать в питоне.

Могу дополнительно выводить в бинарный файл размер массива.

Как такое можно сделать?

struct.unpack('f', file.read(4))

Могу дополнительно выводить в бинарный файл размер массива

Желательно. Ведь он не факт что «квадратный»?

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Не понимаю как массив считать... У меня там двумерный массив записан (34, 56). Какой формат использовать?

Closius
() автор топика
Ответ на: комментарий от I-Love-Microsoft

https://drive.google.com/open?id=0B5LghJXC3XnNNmFIcEdldjZ3bW8

тут фортрановский код, экзешник, бинарник и то что я пытаюсь на питоне сделать (через numpy.fromfile, так как мне надо будет работать с очень большими массивами)

xxx11.bin - бинарник в котором лежат такие данные(первые две цифры это размеры массива):

4 7 1.000000 0.5000000 0.3333333 0.2500000 0.2000000 0.1666667 0.1428571 2.000000 1.000000 0.6666667 0.5000000 0.4000000 0.3333333 0.2857143 3.000000 1.500000 1.000000 0.7500000 0.6000000 0.5000000 0.4285714 4.000000 2.000000 1.333333 1.000000 0.8000000 0.6666667 0.5714286

Closius
() автор топика
Ответ на: комментарий от I-Love-Microsoft

решил вот так (первые два числа в бинарнике - число строк и столбцов массива):

import numpy as np

from scipy.io import FortranFile

f = FortranFile("xxx11.bin", "r")

rows = int(f.read_ints(dtype=np.int32))
columns = int(f.read_ints(dtype=np.int32))
print("-------------------------------")
g = f.read_reals(dtype=np.float32)
nnn = np.reshape(g, (rows,columns), order='F')
print(nnn)
f.close()

Closius
() автор топика
f = open('filename', 'rb')
data = f.read() # Считал массив байт
# Теперь можно, к примеру, перегнать его в матрицу:
m = list(list(k[n*i+j] for i in range(n)) for j in range(m))
# где n и m -- количество строк и столбцов в массиве соответственно
evilface ★★
()
Ответ на: комментарий от evilface
f = open('filename', 'rb')
data = f.read() # Считал массив байт
# Теперь можно, к примеру, перегнать его в матрицу:
matrix = list(list(struct.unpack('f', k[n*i+j:n*i+j+4]) for i in range(n)) for j in range(0, m, 4))
# где n и m -- количество строк и столбцов в массиве соответственно
# Либо так, если те дробные числа не float, а double:
matrix = list(list(struct.unpack('d', k[n*i+j:n*i+j+8]) for i in range(n)) for j in range(0, m, 8))
evilface ★★
()
Ответ на: комментарий от evilface

Это хорошо когда данных не много. Но через struct пару гигов данных гнать будет долго мне кажется.

Closius
() автор топика
Ответ на: комментарий от Closius

На самом деле у evilface вообще всё очень плохо, начиная с f.read(), что сразу считывает всё (возможные пару гигов) в байтовую строку, заканчивая списком списков, которые дожрут оставшуюся после считывания память.

aedeph_ ★★
()
Ответ на: комментарий от aedeph_

Ну можно оставить генератор. Будет всё лениво. В любом случае, ТСу массив-то понадобится.

По поводу .read() — был бы рад услышать, как сделать лучше. У меня в питоне практики не очень много.

evilface ★★
()
Ответ на: комментарий от evilface

В любом случае, ТСу массив-то понадобится.

Ага, ему нужен массив, а ты ему предлагаешь строку и список списков. Молодец, чо.

был бы рад услышать, как сделать лучше

Воспользоваться библиотекой, специально сделанной для этого, что и сделал ТС?

Если уж очень хочется руками, то

def read_integer(f): return struct.unpack('i', f.read(np.dtype(np.int32).itemsize))

def read_real(f): return struct.unpack('f', f.read(np.dtype(np.float32).itemsize))

n = read_integer(f)
m = read_integer(f)

arr = np.empty((n, m), dtype=float, order='F')

for i in range(n):
    for j in range(m):
         arr[i, j] = read_real(f)

,что тоже, вообще говоря, не оптимально, так как считывать надо чанками побольше.

aedeph_ ★★
()
Ответ на: комментарий от vvn_black

Да, точно, надо [0] добавить.

edit: А лучше даже что-то вроде,

n, m = struct.unpack('ii', f.read(2 * np.dtype(np.int32).itemsize))

for j in range(m):
    arr[:, j] = struct.unpack('%if' % n, f.read(n * np.dtype(np.float32).itemsize)
aedeph_ ★★
()
Последнее исправление: aedeph_ (всего исправлений: 2)
Ответ на: комментарий от aedeph_

так как считывать надо чанками побольше.

Если без numpy, то можно сразу строку читать:

struct.unpack('f'*n, f.read(4*n))

upd. опередил с редактированием предыдущего поста.

vvn_black ★★★★★
()
Последнее исправление: vvn_black (всего исправлений: 1)
Ответ на: комментарий от Closius

фортрановский код

У Фортрана есть скользкий момент с файлами. У unformatted файлов на самом деле есть формат. Он разный в зависимости от компилятора, поэтому файлы от компилятора Intel могут быть нечитаемы программой, собранной GNU компилятором. Ну и с другими программами там тоже проблемы.

В свежем фортране (2003, кажется) добавили access='stream', который позволит писать данные в сыром виде. Такие уже будет легче читать питоном.

      open(unit=341,file='xxx11.bin',status='replace',
     x       form='unformatted',access='stream')

А, да. Не забывай, что Фортран пишет массивы по столбцам: сначала весь первый столбец, затем второй и так далее. Большинство других языков пишет и читает по строкам.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)

Всем отписавшимся: в фортране бинарные файлы состоят из записей, причём формат записи стандартом не регламентирован. Должна быть возможность определить её длину и, наверное, возможные ошибки связанные с порчей этой самой длины (не уверен). Поэтому в зависимости от версии компилятора в начале и в конце записи добавляются дополнительные байты, которые обычно содержат длину записи. GNU и Intel просто пишут int, равный размеру записи в байтах. Отсюда четыре раза 4 в начале файла: write(341) NumResultSet пишет «4» как длину записи (один integer), «4» как значение NumResultSet и снова «4» --- длина записи. Потом то же для MaxNode, равного 7. Поэтому просто read(), unpack() работать не будет. ТС сделал правильное решение через FortanFile, но следует иметь в виду, что если FortranFile и программа скомпилирована разными компиляторами, то, вообще говоря, совместимость не гарантирована (хотя, конечно, проблемы маловероятны).

На случай если кто-то выпадет сюда из гугла, попиарю свою поделку: http://jini-zh.org/fconv/fconv.html

Jini ★★
()
Последнее исправление: Jini (всего исправлений: 1)
Ответ на: комментарий от evilface

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

// ой, ты не тс, но, похоже, тоже фортранщик!

aol ★★★★★
()
Ответ на: комментарий от Jini

Поэтому в зависимости от версии компилятора в начале и в конце записи добавляются дополнительные байты
Поэтому просто read(), unpack() работать не будет

Спасибо. Это должно быть где-то в стартовом посте по хорошему.

Worron ★★★
()
Ответ на: комментарий от evilface

Вот на счет генератора это хорошо. Последовательное чтение. Хотя хз че лучше. Если бы мне надо было считывать (перерабатывать) сотни гигов, то есть чтоб оно в память не поместилось, то да тут определенно надо последовательное чтение и обработка, а не сразу все в память. Ну и распаралелить это все думаю не особо сложно. У файла же может быть неколько дескрипторов одновременно на чтение.

Кстати хороший вопрос, как тащит данные scipy FortranFile?

Closius
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.