Как выводить список поплавков в двоичный файл в Python
У меня есть список значений с плавающей запятой в Python:
floats = [3.14, 2.7, 0.0, -1.0, 1.1]
Я хотел бы записать эти значения в двоичный файл с использованием 32-разрядной кодировки IEEE. Каков наилучший способ сделать это в Python? В моем списке на самом деле содержится около 200 МБ данных, поэтому лучше всего было бы "не слишком медленно".
Так как существует 5 значений, я просто хочу получить 20-байтовый файл в качестве вывода.
Ответы
Ответ 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
который можно использовать для чтения файла, если вы заранее знаете количество элементов (например, из размера файла или другого механизма).
Ответ 2
Смотрите: Модуль структуры Python
import struct
s = struct.pack('f'*len(floats), *floats)
f = open('file','wb')
f.write(s)
f.close()
Ответ 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-байтовому файлу.
Ответ 5
посмотрите struct.pack_into
Ответ 6
struct.pack() выглядит так, как вам нужно.
http://docs.python.org/library/struct.html
Ответ 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")