2015-04-14 20 views
9

Sto creando un sistema di modifica file e vorrei creare una funzione tell() basata sulla linea invece di una basata su byte. Questa funzione verrebbe utilizzata all'interno di un "con loop" con la chiamata open (file). Questa funzione è parte di una classe che ha:Come risolvere "OSError: dire posizione disabilitata da next() chiama"

self.f = open(self.file, 'a+') 
# self.file is a string that has the filename in it 

Quello che segue è la funzione originale (Essa ha anche un ambiente char se si voleva riga e un ritorno byte):

def tell(self, char=False): 
    t, lc = self.f.tell(), 0 
    self.f.seek(0) 
    for line in self.f: 
     if t >= len(line): 
      t -= len(line) 
      lc += 1 
     else: 
      break 
    if char: 
     return lc, t 
    return lc 

Il problema che Sto avendo questo è che questo restituisce un OSError e ha a che fare con il modo in cui il sistema sta iterando sul file, ma non capisco il problema. Grazie a tutti coloro che possono aiutare.

+0

Difficile rispondere senza vedere il resto della classe. (Non riuscivo a riprodurlo su Linux usando solo le funzioni.) Potresti voler leggere su [Attributi di OSError'] (https://docs.python.org/3/library/exceptions.html#OSError) , che può darti (e noi) alcune informazioni aggiuntive. La mia prima domanda sarebbe, poiché si tratta di un errore _OS_: qual è il tuo sistema operativo? Inoltre (possibilmente correlato): Perché/come stai [aprendo il file in modalità append] (https://docs.python.org/3/library/functions.html#open) e poi cerchiamo di aggirarlo al suo interno? –

+0

Lo sto aprendo in modalità append perché, si presume che il file sia inesistente prima che l'istanza venga creata. (come sai, sono sicuro che la modalità 'a' crea il file se non esiste ancora). Volevo essere in grado di risparmiare spazio nel codice per avere un controllo se il file esisteva. Il mio sistema operativo è Mac OS X Yosemite, ma non penso che abbia a che fare con Apple. –

risposta

10

Ho una vecchia versione di Python 3, e io sono su Linux invece di un Mac, ma sono stato in grado di ricreare qualcosa di molto vicino al vostro errore:

IOError: telling position disabled by next() call 

un errore IO, non un errore OS, ma per il resto lo stesso. Stranamente, non ho potuto causarlo usando il tuo open('a+', ...), ma solo aprendo il file in modalità lettura: open('r+', ...).

ulteriormente le cose confusionaria è che l'errore viene da _io.TextIOWrapper, una classe che sembra da definire nel file di Python _pyio.py ... sottolineo "Viene visualizzato", perché:

  1. Il TextIOWrapper in quel il file ha attributi come _telling che non riesco ad accedere all'oggetto che si chiama se stesso _io.TextIOWrapper.

  2. La classe TextIOWrapper in _pyio.py non fa alcuna distinzione tra file leggibili, scrivibili o ad accesso casuale. O entrambi dovrebbero funzionare, o entrambi dovrebbero aumentare lo stesso IOError.

Indipendentemente, la classe TextIOWrapper come descritto nel _pyio.py fascicolo disabilita il metodo tell mentre l'iterazione è in corso.Questo sembra essere quello che si sta eseguendo in (i commenti sono miei):

def __next__(self): 
    # Disable the tell method. 
    self._telling = False 
    line = self.readline() 
    if not line: 
     # We've reached the end of the file... 
     self._snapshot = None 
     # ...so restore _telling to whatever it was. 
     self._telling = self._seekable 
     raise StopIteration 
    return line 

nel metodo tell, è quasi sempre fuori break dell'iterazione prima che raggiunga il fine del file, lasciando _telling disabilitato (False):

Un altro modo per ripristinare _telling è il metodo flush, ma anche non è riuscito se chiamato mentre l'iterazione era in corso:

IOError: can't reconstruct logical file position 

Il modo per aggirare questo, almeno sul mio sistema, è quello di chiamata seek(0) sul TextIOWrapper, che restituisce tutto a uno stato noto (e con successo le chiamate flush in giunta):

def tell(self, char=False): 
    t, lc = self.f.tell(), 0 
    self.f.seek(0) 
    for line in self.f: 
     if t >= len(line): 
      t -= len(line) 
      lc += 1 
     else: 
      break 
    # Reset the file iterator, or later calls to f.tell will 
    # raise an IOError or OSError: 
    f.seek(0) 
    if char: 
     return lc, t 
    return lc 

Se questo non è il soluzione per il tuo sistema, potrebbe almeno dirti dove iniziare a cercare.

PS: è necessario considerare sempre restituendo sia il numero di riga che l'offset di carattere. Le funzioni che possono restituire tipi completamente diversi sono difficili da gestire --- è molto più semplice per il chiamante buttare via il valore di lei o lei non ne ha bisogno.

+0

Grazie mille per il tuo aiuto! Quello che sembra essere il mio problema è che non posso chiamare il metodo (built-in) tell() durante l'iterazione di un file (riga per riga). Ho trovato un modo per aggirare questo e la tua risposta mi ha davvero aiutato. Grazie ancora! –

+0

@BrandonGomes: ti dispiacerebbe condividere la tua soluzione con me? – marscher

+0

sorry @marscher Non ho più questo codice. Viene da un vecchio computer. Penso che la risposta fosse di memorizzare alcuni metadati sull'iteratore di file. È sempre possibile riscrivere la funzione __next__. –

8

Non so se questo è stato l'errore originale, ma si può ottenere lo stesso errore se si tenta di chiamare f.tell() all'interno di un'iterazione di linea per linea di un file in questo modo:

with open(path, "r+") as f: 
    for line in f: 
    f.tell() #OSError 

che può essere facilmente sostituito dal seguente:

with open(path, mode) as f: 
    line = f.readline() 
    while line: 
    f.tell() #returns the location of the next line 
    line = f.readline() 
2

un semplice trucco rapida per questo problema:

Come si effettua l'iterazione il file dall'inizio in ogni modo, basta tenere TR ack di dove siete con una variabile dedicato:

file_pos = 0 
with open('file.txt', 'rb') as f: 
    for line in f: 
     # process line 
     file_pos += len(line) 

Ora file_pos sarà sempre, quello che sarebbe file.tell()dire voi. Si noti che questo funziona solo per i file ASCII come tell e cercare lavoro con posizioni di byte. Lavorando su una base è facile però convertire stringhe da byte a stringhe unicode.