Читання цілих чисел із двійкового файлу в 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, який ви, можливо, вже встановили. Спробуйте: імпортувати зображення
Джаред Апдайк,

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 Чи є причина, по якій ти віддаєш перевагу одному шляху перед іншим? Чи є якась логічна різниця?
Калтор,

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 #, complex # або визначений користувачем тип. Подивитисяnumpy.fromfile .

Особисто віддаю перевагу використанню NumPy для роботи з даними масивів / матриць, оскільки це набагато швидше, ніж використання списків Python.


13
Відкриття файлу можна пропустити:a = np.fromfile('file.bin', dtype=np.uint32)
Матьє Шопфер

16

Починаючи з Python 3.2+, ви також можете досягти цього, використовуючи from_bytesвласний метод int:

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

Зверніть увагу, що ця функція вимагає вказати, чи кодується число у форматі великого чи малого ендіана, тому вам доведеться визначити ендіан, щоб переконатися, що він працює правильно.


6

Крім structви також можете використовувати arrayмодуль

import array
values = array.array('l') # array of long integers
values.read(fin, 1) # read 1 integer
file_size  = values[0]

Влучне зауваження. Але це рішення не настільки гнучке, як у модуля struct, оскільки всі елементи, що читаються через values.read (), повинні мати довгі цілі числа (не зручно читати довге ціле число, байт, а потім довге ціле число, з модуль масиву).
Eric O Lebigot

Я згоден. arrayце ефективний спосіб читати двійковий файл, але не дуже гнучкий, коли нам доводиться мати справу зі структурою, як ви правильно зазначили.
Нік Дандулакіс

1
array.read припинено на користь array.fromfile з 1.51

4

Під час читання бінарного файлу вам потрібно розпакувати його у ціле число, тому використовуйте для цього модуль struct

import struct
fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size, = struct.unpack("i",fin.read(4))

struct.unpack повертає кортеж
luc

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))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.