¿Por qué no puedo llamar a read () dos veces en un archivo abierto?

98

Para un ejercicio que estoy haciendo, estoy tratando de leer el contenido de un archivo dado dos veces usando el read()método. Curiosamente, cuando lo llamo por segunda vez, ¿no parece devolver el contenido del archivo como una cadena?

Aqui esta el codigo

f = f.open()

# get the year
match = re.search(r'Popularity in (\d+)', f.read())

if match:
  print match.group(1)

# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', f.read())

if matches:
  # matches is always None

Por supuesto, sé que esta no es la mejor o más eficiente manera, este no es el punto aquí. El punto es, ¿por qué no puedo llamar read()dos veces? ¿Tengo que restablecer el identificador del archivo? ¿O cerrar / volver a abrir el archivo para hacer eso?

método de ayuda
fuente
2
¿De dónde sacaste la idea de que la lectura no cambiaría el estado del archivo? ¿Qué referencia o tutorial estás usando?
S.Lott
Creo que cerrar y volver a abrir el archivo debería funcionar según las respuestas a continuación.
Anthony
@Shynthriir: Cerrar y volver a abrir el archivo no siempre es una buena idea ya que puede tener otros efectos en el sistema (archivos temporales, incron, etc.).
Ignacio Vazquez-Abrams
3
Solo quiero decir lo obvio: ¡ HAS llamado a read () dos veces!
4
W / R / T / S.Lott, y a partir de 5 años: esto realmente debe estar en la documentación de Python. No es obvio que uno deba asumir que leer un objeto de archivo cambiaría el estado de cualquier cosa, especialmente si uno está acostumbrado a trabajar con datos inmutables / programación de estilo funcional ...
Paul Gowder

Respuestas:

156

La llamada read()lee todo el archivo y deja el cursor de lectura al final del archivo (sin nada más para leer). Si usted está buscando para leer un cierto número de líneas a la vez se puede usar readline(), readlines()o se puede recorrer con líneas for line in handle:.

Para responder a su pregunta directamente, una vez que se ha leído un archivo, con read()puede usar seek(0)para devolver el cursor de lectura al inicio del archivo (los documentos están aquí ). Si sabe que el archivo no será demasiado grande, también puede guardar la read()salida en una variable, usándola en sus expresiones findall.

PD. No olvide cerrar el archivo una vez que haya terminado con él;)

Tim
fuente
4
+1, sí, lea la variable temporal para evitar E / S de archivos innecesarias. Es una economía falsa que esté guardando memoria porque tiene menos variables (explícitas).
Nick T
2
@NickT: Esperaría que un archivo pequeño que se lee varias veces sea almacenado en caché por el sistema operativo (al menos en Linux / OSX), por lo que no hay E / S de archivos adicionales para leer dos veces. Los archivos grandes que no caben en la memoria no se almacenan en caché, pero no desea leerlos en una variable porque comenzará a intercambiar. Entonces, en caso de duda, lea siempre varias veces. Si está seguro de que los archivos son pequeños, haga lo que ofrezca el mejor programa.
Claude
3
El desmontaje se puede automatizar con with.
Cees Timmerman
30

sí, como arriba ...

escribiré solo un ejemplo:

>>> a = open('file.txt')
>>> a.read()
#output
>>> a.seek(0)
>>> a.read()
#same output
Hormiga
fuente
17

Todos los que han respondido a esta pregunta hasta ahora tienen toda la razón: read()recorren el archivo, por lo que después de llamarlo, no puede volver a llamarlo.

Lo que agregaré es que, en su caso particular, no es necesario volver al inicio o volver a abrir el archivo, simplemente puede almacenar el texto que ha leído en una variable local y usarlo dos veces, o tantas veces como quieras, en tu programa:

f = f.open()
text = f.read() # read the file into a local variable
# get the year
match = re.search(r'Popularity in (\d+)', text)
if match:
  print match.group(1)
# get all the names
matches = re.findall(r'<td>(\d+)</td><td>(\w+)</td><td>(\w+)</td>', text)
if matches:
  # matches will now not always be None
Tom Anderson
fuente
1
+1 En realidad, esta fue la solución propuesta para este ejercicio ( code.google.com/intl/de-DE/edu/languages/google-python-class/… ). Pero de alguna manera no pensé en almacenar la cadena en una variable. ¡Oh!
helpermethod
1
Con Python3, use pathlib. from pathlib import Path; text = Path(filename).read_text()Se encarga de abrir, cerrar, etc.
PaulMcG
14

El puntero de lectura se mueve después del último byte / carácter leído. Utilice el seek()método para rebobinar el puntero de lectura hasta el principio.

Ignacio Vázquez-Abrams
fuente
2

Cada archivo abierto tiene una posición asociada.
Cuando lee () lee desde esa posición. Por ejemplo, read(10)lee los primeros 10 bytes de un archivo recién abierto, luego otro read(10)lee los siguientes 10 bytes. read()sin argumentos lee todo el contenido del archivo, dejando la posición del archivo al final del archivo. La próxima vez que llame read()no hay nada que leer.

Puede utilizar seekpara mover la posición del archivo. O probablemente lo mejor en su caso sería hacer una read()y mantener el resultado de ambas búsquedas.

Douglas Leeder
fuente
1

read() consume . Por lo tanto, puede restablecer el archivo o buscar el inicio antes de volver a leer. O, si se adapta a su tarea, puede usar read(n)para consumir solo nbytes.

Towi
fuente
1

Siempre encuentro el método de lectura como un paseo por un callejón oscuro. Bajas un poco y te detienes pero si no estás contando tus pasos no estás seguro de qué tan lejos estás. Seek da la solución reposicionando, la otra opción es Tell, que devuelve la posición a lo largo del archivo. Puede ser que la api del archivo Python pueda combinar lectura y búsqueda en un read_from (posición, bytes) para hacerlo más simple; hasta que eso suceda, debe leer esta página .

¿Qué?
fuente