Cómo resolver “OSError: decir la posición deshabilitada por la siguiente () llamada”

Estoy creando un sistema de edición de archivos y me gustaría hacer una función tell () basada en líneas en lugar de una basada en bytes. Esta función se usaría dentro de un “con bucle” con la llamada abierta (archivo). Esta función es parte de una clase que tiene:

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

La siguiente es la función original (también tiene una configuración de caracteres si desea que la línea y el retorno de bytes):

 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 

El problema que tengo con esto es que devuelve un OSError y tiene que ver con cómo el sistema está iterando sobre el archivo pero no entiendo el problema. Gracias a cualquiera que pueda ayudar.

Tengo una versión anterior de Python 3, y estoy usando Linux en lugar de una Mac, pero pude recrear algo muy cerca de tu error:

 IOError: telling position disabled by next() call 

Un error de E / S , no un error del sistema operativo , pero por lo demás es el mismo. Por extraño que parezca, no pude provocarlo utilizando su open('a+', ...) , pero solo al abrir el archivo en modo de lectura: open('r+', ...) .

Lo que más confunde es que el error proviene de _io.TextIOWrapper , una clase que parece estar definida en el archivo _pyio.py de Python … Hago hincapié en “aparece”, porque:

  1. El TextIOWrapper en ese archivo tiene atributos como _telling que no puedo acceder en el objeto _io.TextIOWrapper -it-is que se llama a sí mismo _io.TextIOWrapper .

  2. La clase TextIOWrapper en _pyio.py no hace ninguna distinción entre archivos legibles, grabables o de acceso aleatorio. Cualquiera de los dos debería funcionar, o ambos deberían elevar el mismo IOError .

Independientemente, la clase TextIOWrapper , como se describe en el archivo _pyio.py , desactiva el método tell mientras la iteración está en curso . Esto parece ser lo que estás encontrando (los comentarios son míos):

 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 restre _telling to whatever it was. self._telling = self._seekable raise StopIteration return line 

En su método tell , casi siempre break sale de la iteración antes de que llegue al final del archivo, dejando _telling deshabilitado ( False ):

Otra forma de restablecer _telling es el método de flush , pero también falló si se llamaba mientras la iteración estaba en curso:

 IOError: can't reconstruct logical file position 

La forma de evitar esto, al menos en mi sistema, es llamar a seek(0) en el TextIOWrapper , que restaura todo a un estado conocido (y con éxito llama al flush en el negocio):

 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 

Si esa no es la solución para su sistema, al menos podría decirle dónde comenzar a buscar.

PD: debería considerar devolver siempre tanto el número de línea como el desplazamiento de caracteres. Es difícil lidiar con las funciones que pueden devolver tipos completamente diferentes: es mucho más fácil para la persona que llama simplemente deshacerse del valor que ella o ella no necesita.

No sé si este fue el error original, pero puede obtener el mismo error si intenta llamar a f.tell () dentro de una iteración línea a línea de un archivo como este:

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

Que puede ser fácilmente sustituido por lo siguiente:

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

Solo una solución rápida para este problema:

De todos modos, al estar repitiendo sobre el archivo desde el principio, simplemente haga un seguimiento de dónde se encuentra con una variable dedicada:

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

Ahora file_pos siempre será, lo que file.tell() te dirá . Tenga en cuenta que esto solo funciona para archivos ASCII, ya que indica y busca trabajo con posiciones de bytes. Trabajar sobre una base de línea es fácil, sin embargo, convertir cadenas de bytes a cadenas de Unicode.