2010-07-10 11 views
13

Sto leggendo in un file binario (un jpg in questo caso) e ho bisogno di trovare alcuni valori in quel file. Per chi fosse interessato, il file binario è un jpg e sto cercando di individuarne le dimensioni cercando la struttura binaria come detailed here.Python: ricerca/lettura di dati binari

Ho bisogno di trovare FFC0 nei dati binari, saltare un certo numero di byte e quindi leggere 4 byte (questo dovrebbe darmi le dimensioni dell'immagine).

Qual è un buon metodo per cercare il valore nei dati binari? Esiste un equivalente di "trova" o qualcosa del genere?

+1

hai mai guardato nell'immaginario? IIRC c'è anche una libreria python per questo. – txwikinger

+0

Ho, e funziona benissimo, ma è abbastanza pesante per trovare solo le dimensioni del file. – Parand

+1

dovresti usare un modulo appropriato per qualcosa di simile a questo http://snippets.dzone.com/posts/show/1021 –

risposta

7

È possibile caricare il file in una stringa e cercare quella stringa per la sequenza di byte 0xffc0 utilizzando il metodo str.find(). Funziona per qualsiasi sequenza di byte.

Il codice per eseguire questa operazione dipende da un paio di cose. Se apri il file in modalità binaria e stai usando Python 3 (entrambi sono probabilmente le migliori pratiche per questo scenario), dovrai cercare una stringa di byte (al contrario di una stringa di caratteri), il che significa che tu prefisso la stringa con b.

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

Se si apre il file in modalità testo in Python 3, che avrebbe dovuto cercare una stringa di caratteri:

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

anche se non c'è nessun motivo particolare per farlo. Non ti dà alcun vantaggio rispetto al modo precedente, e se ti trovi su una piattaforma che tratta file binari e file di testo in modo diverso (ad esempio Windows), c'è la possibilità che ciò possa causare problemi.

Python 2 non fa la distinzione tra stringhe di byte e stringhe di caratteri, quindi se si sta utilizzando quella versione, non importa se si include o si esclude lo b in b'\xff\xc0'. E se la tua piattaforma tratta file binari e file di testo in modo identico (ad esempio Mac o Linux), non importa se si utilizza 'r' o 'rb' come modalità file. Ma raccomanderei comunque di usare qualcosa come il primo esempio di codice sopra solo per compatibilità diretta - nel caso in cui dovessi passare a Python 3, è una cosa in meno da risolvere.

+6

Se si tratta di un file veramente grande, non è una buona idea leggerlo in una stringa tutto in una volta. – icktoofay

+2

Dubito che sia così grande che sarà un problema. –

+3

Dato che sto cercando solo il primo frame, probabilmente leggerò una piccola parte del file e lo elaborerò invece di leggere l'intero file. – Parand

4

Il re modulo fa lavoro sia con stringa e binari dati (str in Python 2 e bytes in Python 3), in modo da poter utilizzare così come str.find per il vostro compito.

2

Bene, ovviamente c'è PIL Il modulo Immagine ha le dimensioni come attributo. Se desideri ottenere le dimensioni esattamente come suggerisci e senza caricare il file, dovrai eseguirlo riga per riga. Non è il modo più bello per farlo, ma funzionerebbe.

6

Il modulo bitstring è stato progettato per questo scopo. Per il vostro caso il seguente codice (che non ho ancora testato) dovrebbe contribuire a illustrare:

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

Quindi 'Bits.find' restituisce solo un valore booleano e imposta l'attributo' Bits.bytepos'? Forse nella documentazione del modulo dovresti avvertire che 'bitstring' non è thread-safe (non che sia importante in questa risposta, ovviamente). – tzot

+0

@ ΤΖΩΤΖΙΟΥ: Sì, hai un buon punto. Non trovo sorprendente che i metodi di muting oi metodi di lettura non siano thread-safe, ma si potrebbe ragionevolmente prevedere che l'uso di "find" su un oggetto immutabile bit-saggio sia ragionevole. Per essere onesti non è mai spuntato prima, ma è qualcosa a cui pensare ... –

+0

Solo un'idea: 'find' potrebbe restituire un oggetto con tutte le informazioni necessarie, à la' re.match' e 're.search'. Si potrebbe avere questa classe "BitMatch" essere una sottoclasse di 'bool', per compatibilità all'indietro. – tzot

1

Invece di leggere l'intero file in memoria, la ricerca e poi a scrivere un nuovo file su disco è possibile utilizzare il mmap modulo per questo. mmap sarà non memorizza l'intero file in memoria e consente la modifica sul posto.

#!/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

Python> = 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

Il metodo find() dovrebbe essere usato solo se avete bisogno di conoscere la posizione del sub, in caso contrario, è possibile utilizzare l'operatore in, ad esempio:

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

Questo lo ha fatto per me - stavo cercando di confrontare una stringa con una stringa di byte. Tutto quello che dovevo fare era mettere la b davanti al mio termine di ricerca ed è stata trovata all'interno della stringa di byte. – pa1983

0

In Python 3.x è possibile cercare una stringa di byte da un altro stringa di byte in questo modo:

>>> 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 
>>>