2010-07-10 6 views
13

Я читаю в двоичном файле (в данном случае jpg) и вам нужно найти некоторые значения в этом файле. Для тех, кто заинтересован, двоичный файл - это jpg, и я пытаюсь выбрать его размеры, ища двоичную структуру как detailed here.Python: Поиск/чтение двоичных данных

Мне нужно найти FFC0 в двоичных данных, пропустить некоторое количество байтов, а затем прочитать 4 байта (это должно дать мне размеры изображения).

Что такое хороший способ поиска значения в двоичных данных? Есть ли эквивалент «найти», или что-то вроде re?

+1

Вы когда-нибудь заглядывали в воображение? IIRC есть также библиотека python для этого. – txwikinger

+0

У меня есть, и он отлично работает, но довольно просто найти размеры файла. – Parand

+1

вы должны использовать модуль, подходящий для чего-то вроде этого http://snippets.dzone.com/posts/show/1021 –

ответ

7

Фактически вы можете загрузить файл в строку и выполнить поиск этой строки для последовательности байтов 0xffc0 с использованием метода str.find(). Он работает для любой последовательности байтов.

Код для этого зависит от пары вещей. Если вы откроете файл в двоичном режиме, и вы используете Python 3 (оба из которых, вероятно, являются наилучшей практикой для этого сценария), вам нужно будет искать строку байтов (в отличие от символьной строки), что означает, что вы должны префикс строки b.

with open(filename, 'rb') as f: 
    s = f.read() 
s.find(b'\xff\xc0') 

Если открыть файл в текстовом режиме в Python 3, вы должны искать строку символов:

with open(filename, 'r') as f: 
    s = f.read() 
s.find('\xff\xc0') 

хотя нет особых причин, чтобы сделать это. Это не дает вам преимуществ по сравнению с предыдущим способом, и если вы находитесь на платформе, которая обрабатывает двоичные файлы и текстовые файлы по-разному (например, Windows), есть вероятность, что это вызовет проблемы.

Python 2 не делает различия между строками байтов и символьными строками, поэтому, если вы используете эту версию, неважно, включите или исключите b в b'\xff\xc0'. И если ваша платформа обрабатывает двоичные файлы и текстовые файлы одинаково (например, Mac или Linux), не имеет значения, используете ли вы 'r' или 'rb' в качестве файлового режима. Но я бы порекомендовал использовать что-то вроде первого примера кода выше для простоты совместимости - в случае, если вы когда-либо переключитесь на Python 3, это еще не все, что нужно исправить.

+6

Если это действительно большой файл, не так-то просто прочитать его в строке сразу. – icktoofay

+2

Я сомневаюсь, что он такой большой, что это будет проблемой. –

+3

Поскольку я ищу только первый кадр, я, скорее всего, смогу прочитать небольшую часть файла и обработать, а не читать весь файл. – Parand

4

re модуль делает работы как с строкой и бинарными данными (str в Python 2 и bytes в Python 3), так что вы можете использовать его, а также str.find для вашей задачи.

2

Ну, очевидно, есть PIL Модуль Image имеет размер как атрибут. Если вы хотите получить размер именно так, как вы предлагаете, и без загрузки файла вам придется проходить его по очереди. Не самый приятный способ сделать это, но это сработает.

6

Модуль bitstring был разработан для этой цели. В вашем случае следующий код (который я не проверял) должен помочь проиллюстрировать:

from bitstring import ConstBitStream 
# Can initialise from files, bytes, etc. 
s = ConstBitStream(filename='your_file') 
# Search to Start of Frame 0 code on byte boundary 
found = s.find('0xffc0', bytealigned=True) 
if found: 
    print("Found start code at byte offset %d." % found[0]) 
    s0f0, length, bitdepth, height, width = s.readlist('hex:16, uint:16, 
                 uint:8, 2*uint:16') 
    print("Width %d, Height %d" % (width, height)) 
+0

Итак, 'Bits.find' возвращает только логическое значение и устанавливает атрибут' Bits.bytepos'? Возможно, в документации по модулю вы должны предупредить, что «bitstring» не является потокобезопасным (не то, что это важно в этом ответе, конечно). – tzot

+0

@ ΤΖΩΤΖΙΟΥ: Да, у вас есть хороший момент. Я не удивляюсь, что методы мутирования или методы чтения не являются потокобезопасными, но разумно можно ожидать, что использование «find» на побитом неизменяемом объекте может быть разумным. Честно говоря, это никогда не возникало, но о чем-то думать ... –

+0

Просто идея: 'find' может вернуть объект со всей необходимой информацией, à la' re.match' и 're.search'. Этот класс «BitMatch» может быть подклассом «bool» для обратной совместимости. – tzot

1

Вместо того, чтобы читать весь файл в память, разыскивая его, а затем писать новый файл на диск можно использовать ММАП модуль для этого. mmap будет не хранит весь файл в памяти и позволяет изменять его на месте.

#!/usr/bin/python 

import mmap 

with open("hugefile", "rw+b") as f: 
    mm = mmap.mmap(f.fileno(), 0) 
    print mm.find('\x00\x09\x03\x03') 
0

питон> = 3,2

import re 

f = open("filename.jpg", "rb") 
byte = f.read() 
f.close() 

matchObj = re.match(b'\xff\xd8.*\xff\xc0...(..)(..).*\xff\xd9', byte, re.MULTILINE|re.DOTALL) 
if matchObj: 
    # http://stackoverflow.com/questions/444591/convert-a-string-of-bytes-into-an-int-python 
    print (int.from_bytes(matchObj.group(1), 'big')) # height 
    print (int.from_bytes(matchObj.group(2), 'big')) # width 
1

find() метод следует использовать только, если вам нужно знать положение подлодки, если нет, то вы можете использовать оператор in, например:

with open("foo.bin", 'rb') as f: 
    if b'\x00' in f.read(): 
     print('The file is binary!') 
    else: 
     print('The file is not binary!') 
+1

Это сделало это для меня - я пытался сравнить строку с байтовой строкой. Все, что мне нужно было сделать, это поставить b перед моим поисковым термином, и он был найден в строке байта. – pa1983

0

В Python 3.x вы можете найти строку байт другой байт строки, как это:

>>> byte_array = b'this is a byte array\r\n\r\nXYZ\x80\x04\x95 \x00\x00\x00\x00\x00' 
>>> byte_array.find('\r\n\r\n'.encode()) 
20 
>>> 
Смежные вопросы