Подтвердить что ты не робот

Как выводить список поплавков в двоичный файл в Python

У меня есть список значений с плавающей запятой в Python:

floats = [3.14, 2.7, 0.0, -1.0, 1.1]

Я хотел бы записать эти значения в двоичный файл с использованием 32-разрядной кодировки IEEE. Каков наилучший способ сделать это в Python? В моем списке на самом деле содержится около 200 МБ данных, поэтому лучше всего было бы "не слишком медленно".

Так как существует 5 значений, я просто хочу получить 20-байтовый файл в качестве вывода.

4b9b3361

Ответ 1

Алекс абсолютно прав, так эффективнее это сделать:

from array import array
output_file = open('file', 'wb')
float_array = array('d', [3.14, 2.7, 0.0, -1.0, 1.1])
float_array.tofile(output_file)
output_file.close()

А затем прочитайте массив так:

input_file = open('file', 'rb')
float_array = array('d')
float_array.fromstring(input_file.read())

Объекты array.array также имеют метод .fromfile который можно использовать для чтения файла, если вы заранее знаете количество элементов (например, из размера файла или другого механизма).

Ответ 3

Модуль массива в стандартной библиотеке может быть более подходящим для этой задачи, чем модуль структуры, который все предлагают. Производительность с 200 МБ данных должна быть значительно лучше с массивом.

Если вы хотите воспользоваться множеством опций, попробуйте профилировать в своей системе что-то вроде этого

Ответ 4

Я не уверен, как NumPy будет сравнивать производительность для вашего приложения, но, возможно, стоит изучить.

Использование NumPy:

from numpy import array
a = array(floats,'float32')
output_file = open('file', 'wb')
a.tofile(output_file)
output_file.close()

также приводит к 20-байтовому файлу.

Ответ 7

Я столкнулся с подобной проблемой, нечаянно записывая CSV файл на 100+ ГБ. Ответы здесь были очень полезны, но, чтобы понять его, Я профилировал все упомянутые решения, а затем некоторые. Все профайлинговые прогоны были выполнены на Macbook Pro 2014 с SSD с использованием python 2.7. Из того, что я вижу, подход struct определенно является самым быстрым с точки зрения производительности:

6.465 seconds print_approach    print list of floats
4.621 seconds csv_approach      write csv file
4.819 seconds csvgz_approach    compress csv output using gzip
0.374 seconds array_approach    array.array.tofile
0.238 seconds numpy_approach    numpy.array.tofile
0.178 seconds struct_approach   struct.pack method

Ответ 8

Мой "ответ" - это действительно комментарий к различным ответам. Я не могу комментировать, так как у меня нет 50 репутации.

Если файл должен быть прочитан обратно Python, используйте модуль "pickle". Этот инструмент может читать и писать много вещей в двоичном виде.

Но то, как задают вопрос, говоря "32-битное кодирование IEEE", звучит так, будто файл будет прочитан на других языках. В этом случае порядок байтов должен быть указан. Проблема состоит в том, что большинство машин - x86 с порядком байтов с прямым порядком байтов, но язык обработки данных номер один - Java/JVM, использующий порядок байтов с прямым порядком байтов. Таким образом, Python "tofile()" будет использовать C, который использует младший порядок байтов из-за того, что машина имеет младший порядок байтов, а затем код обработки данных на Java/JVM будет декодироваться с использованием байтов с прямым порядком байтов, что приведет к ошибке.

Для работы с JVM:

# convert to bytes, BIG endian, for use by Java
import struct
f = [3.14, 2.7, 0.0, -1.0, 1.1]
b = struct.pack('>'+'f'*len(f), *f)

with open("f.bin", "wb") as file:
    file.write(b)

На стороне Java:

try(var stream = new DataInputStream(new FileInputStream("f.bin")))
{
    for(int i = 0; i < 5; i++)
        System.out.println(stream.readFloat());
}
catch(Exception ex) {}

Теперь проблема в коде Python 'f'*len(f) - надеюсь, интерпретатор на самом деле не создаст сверхдлинную строку "ffffff...".

Я бы использовал массив numpy и byteswap

import numpy, sys
f = numpy.array([3.14, 2.7, 0.0, -1.0, 1.1], dtype=numpy.float32)

if sys.byteorder == "little":
    f.byteswap().tofile("f.bin") # using BIG endian, for use by Java
else:
    f.tofile("f.bin")