2013-05-19 23 views
9

Python 3. Sto usando il widget di dialogo file di QT per salvare i PDF scaricati da internet. Ho letto il file usando 'open' e sto provando a scriverlo usando il widget di dialogo dei file. Tuttavia, ho eseguito un errore "TypeError:" _io.BufferedReader "non supporta l'interfaccia del buffer".Python che scrive file binari, byte

codice Esempio:

with open('file_to_read.pdf', 'rb') as f1: 
    with open('file_to_save.pdf', 'wb') as f2: 
     f2.write(f1) 

Questa logica funziona correttamente con i file di testo quando non si utilizza la 'b' designatore, o durante la lettura di un file dal web, come con urllib o richieste. Questi sono del tipo 'bytes', che penso di aver bisogno di aprire il file come. Invece, si sta aprendo come lettore bufferizzato. Ho provato i byte (f1), ma ottenere "TypeError: 'byte' oggetto non può essere interpretato come un intero." Qualche idea?

+1

Basta provare - 'data = list (f1.read())' e 'f2.write (data)' – karthikr

+0

'list' evidentemente non supporta nemmeno l'interfaccia buffer. –

risposta

10

Se il vostro intento è quello di fare semplicemente una copia del file, è possibile utilizzare shutil

>>> import shutil 
>>> shutil.copyfile('file_to_read.pdf','file_to_save.pdf') 

Oppure, se avete bisogno di accedere byte per byte, simile alla vostra struttura, questo funziona:

>>> with open('/tmp/fin.pdf','rb') as f1: 
... with open('/tmp/test.pdf','wb') as f2: 
...  while True: 
...   b=f1.read(1) 
...   if b: 
...    # process b if this is your intent 
...    n=f2.write(b) 
...   else: break 

Ma byte per byte è potenzialmente molto lento.

Oppure, se si desidera un buffer che accelerare l'operazione (senza correre il rischio di leggere un file di dimensioni sconosciute completamente nella memoria):

>>> with open('/tmp/fin.pdf','rb') as f1: 
... with open('/tmp/test.pdf','wb') as f2: 
...  while True: 
...   buf=f1.read(1024) 
...   if buf: 
...    for byte in buf: 
...     pass # process the bytes if this is what you want 
...       # make sure your changes are in buf 
...    n=f2.write(buf) 
...   else: 
...    break 

Con Python 2.7+ o 3.1+ è anche possibile utilizzare questa scorciatoia (invece di usare due with blocchi):

with open('/tmp/fin.pdf','rb') as f1,open('/tmp/test.pdf','wb') as f2: 
    ... 
+0

Grazie - la tua 2a e 3a soluzione hanno funzionato entrambe. (Non è possibile usare il file di copia a causa del modo in cui funziona la finestra di salvataggio di QT) Usare semplicemente .read() sul bufferedReader per convertire in byte sembrava funzionare altrettanto bene - pensavo di averlo provato. Ho imparato qualcosa di nuovo dai tuoi esempi. –

+1

Fai attenzione, il nome variabile 'bytes' può entrare in collisione con il tipo predefinito di Python' bytes' che rappresenta i dati binari in Python 3.x! (In Python 2.7 è solo un alias di 'str') – minmaxavg

+0

@minmaxavg: hai ragione, e cambierò quando avrò una possibilità. Grazie! – dawg

5

E 'davvero non ha senso scrivere un file in un altro file. Quello che vuoi è scrivere il contenuto di f1 in f2. Si ottiene il contenuto con f1.read(). Quindi devi fare questo:

with open('file_to_read.pdf', 'rb') as f1: 
    with open('file_to_save.pdf', 'wb') as f2: 
     f2.write(f1.read()) 
+0

Ecco come si presenta la soluzione attuale. –

+3

Mentre funziona, l'intero file viene letto in memoria prima di essere scritto, non molto adatto alla memoria. Come indicato in Python [docs] (http://docs.python.org/2/tutorial/inputoutput.html#methods-of-file-objects) "è il tuo problema se il file è due volte più grande della memoria della tua macchina ' –

2

imparato da python cookbook

from functools import partial 

with open(fpath, 'rb') as f, open(target_fpath, 'wb') as target_f: 
    for _bytes in iter(partial(f.read, 1024), ''): 
     target_f.write(_bytes) 

partial(f.read, 1024) restituisce una funzione, leggere il file binario 1024 byte ad ogni turno. iter terminerà quando si incontra uno blank string ''.