Чтение целых чисел из двоичного файла в Python

82

Я пытаюсь прочитать файл BMP на Python. Я знаю, что первые два байта указывают фирму BMP. Следующие 4 байта - это размер файла. Когда я выполняю:

fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size = int(fin.read(4))  

Я получил:

ValueError: недопустимый литерал для int () с базой 10: 'F # \ x13'

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

Мануэль Араоз
источник
2
Если ваша цель - использовать растровое изображение вместо того, чтобы тратить время на написание собственной библиотеки BMP (не то чтобы это не звучало весело ...), вы можете использовать PIL pythonware.com/products/pil, который вы, возможно, уже установили. Попробуйте: import Image
Джаред Апдайк,
7
Спасибо, Джаред, но я хотел читать BMP вручную только для удовольствия! :)
Мануэль Араоз

Ответы:

123

readМетод возвращает последовательность байтов в виде строки. Чтобы преобразовать из строковой байтовой последовательности в двоичные данные, используйте встроенный structмодуль: http://docs.python.org/library/struct.html .

import struct

print(struct.unpack('i', fin.read(4)))

Обратите внимание, что unpackвсегда возвращает кортеж, поэтому struct.unpack('i', fin.read(4))[0]выдает целое значение, которое вам нужно.

Вероятно, вам следует использовать строку формата '<i'(<- это модификатор, который указывает порядок байтов с прямым порядком байтов, стандартный размер и выравнивание - по умолчанию используется порядок байтов, размер и выравнивание платформы). Согласно спецификации формата BMP, байты должны быть записаны в порядке байтов Intel / little-endian.

кодовая лента
источник
22
Вместо того, чтобы писать, i = struct.unpack(...)[0]я часто пишуi, = struct.unpack(...)
Otto Allmendinger
@Otto Есть какая-то причина, по которой вы предпочитаете один способ другому? Есть ли логическая разница?
Caltor
2
Я нахожу очень удивительным, что в Python нет встроенной функции для чтения целых чисел (или коротких замыканий и т. Д.) Из файла. Я не эксперт по Java, но считаю, что для этого у него есть собственные функции, такие как readUnsignedShort ().
Caltor
@codeape Не могли бы вы определить, что делает [0], пожалуйста, или хотя бы какой это тип языкового элемента. Это не сразу видно, и поиск в документации Python практически невозможен.
Caltor
Для списков и кортежей obj [N] означает: получить N-й элемент obj. См. Docs.python.org/tutorial/introduction.html#lists
codeape
50

Альтернативный метод, который не использует struct.unpack (), - использовать NumPy :

import numpy as np

f = open("file.bin", "r")
a = np.fromfile(f, dtype=np.uint32)

'dtype' представляет тип данных и может быть int #, uint #, float #, сложным # или определяемым пользователем типом. Смотрите numpy.fromfile.

Лично я предпочитаю использовать NumPy для работы с данными массива / матрицы, так как это намного быстрее, чем использование списков Python.

Эмануэль Эй
источник
13
Открытие файла можно пропустить:a = np.fromfile('file.bin', dtype=np.uint32)
Mathieu Schopfer
16

Начиная с Python 3.2+, вы также можете сделать это с помощью from_bytesсобственного метода int:

file_size = int.from_bytes(fin.read(2), byteorder='big')

Обратите внимание, что эта функция требует, чтобы вы указали, в каком формате закодировано число: с прямым или обратным порядком байтов, поэтому вам придется определить порядок байтов, чтобы убедиться, что он работает правильно.

CrepeGoat
источник
6

Кроме того, structвы также можете использовать arrayмодуль

import array
values = array.array('l') # array of long integers
values.read(fin, 1) # read 1 integer
file_size  = values[0]
Ник Дандулакис
источник
Хорошая точка зрения. Но это решение не такое гибкое, как у структурного модуля, поскольку все элементы читаются через values.read () должны быть длинными целыми числами (неудобно читать длинное целое число, байт, а затем длинное целое число с помощью модуль массива).
Эрик О Лебигот,
Я согласен. array- это эффективный способ чтения двоичного файла, но не очень гибкий, когда нам приходится иметь дело со структурой, как вы правильно заметили.
Ник Дандулакис,
1
array.read устарел в пользу array.fromfile с 1.51
4

Когда вы читаете двоичный файл, вам нужно распаковать его в целое число, поэтому используйте для этого struct module

import struct
fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size, = struct.unpack("i",fin.read(4))
Анураг Униял
источник
struct.unpack возвращает кортеж
люк,
1

При чтении из двоичного файла используется тип данных, называемый байтами. Это немного похоже на список или кортеж, за исключением того, что он может хранить только целые числа от 0 до 255.

Пытаться:

file_size = fin.read(4)
file_size0 = file_size[0]
file_size1 = file_size[1]
file_size2 = file_size[2]
file_size3 = file_size[3]

Или же:

file_size = list(fin.read(4))

Вместо:

file_size = int(fin.read(4))
Супер S
источник