Pourquoi ne puis-je pas appeler read () deux fois sur un fichier ouvert?


98

Pour un exercice que je fais, j'essaye de lire le contenu d'un fichier donné deux fois en utilisant la read()méthode. Étrangement, lorsque je l'appelle la deuxième fois, il ne semble pas renvoyer le contenu du fichier sous forme de chaîne?

Voici le code

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

Bien sûr, je sais que ce n'est pas le moyen le plus efficace ou le meilleur, ce n'est pas le but ici. Le fait est que je ne peux pas appeler read()deux fois? Dois-je réinitialiser le descripteur de fichier? Ou fermer / rouvrir le fichier pour ce faire?


2
D'où vous est venue l'idée que la lecture ne changerait pas l'état du fichier? Quelle référence ou quel tutoriel utilisez-vous?
S.Lott

Je pense que la fermeture et la réouverture du fichier devraient fonctionner en fonction des réponses ci-dessous.
Anthony

@Shynthriir: Fermer et rouvrir le fichier n'est pas toujours une bonne idée car cela peut avoir d'autres effets dans le système (fichiers temporaires, incron, etc.).
Ignacio Vazquez-Abrams

3
Je veux juste dire l'évidence: Vous SAVIEZ lecture d'appel () deux fois!

4
W / R / T / S.Lott, et à partir de 5 ans: cela doit vraiment être dans la documentation python. Il n'est pas évident que l'on devrait supposer que la lecture d'un objet fichier changerait l'état de quoi que ce soit, surtout si l'on est habitué à travailler avec des données immuables / une programmation de style fonctionnel ...
Paul Gowder

Réponses:


156

L'appel read()lit le fichier entier et laisse le curseur de lecture à la fin du fichier (sans plus rien à lire). Si vous cherchez à lire un certain nombre de lignes à la fois , vous pouvez utiliser readline(), readlines()ou itérer à travers les lignes avec for line in handle:.

Pour répondre directement à votre question, une fois qu'un fichier a été lu, read()vous pouvez utiliser seek(0)pour ramener le curseur de lecture au début du fichier (les documents sont ici ). Si vous savez que le fichier ne sera pas trop volumineux, vous pouvez également enregistrer la read()sortie dans une variable, en l'utilisant dans vos expressions findall.

Ps. N'oubliez pas de fermer le fichier une fois que vous en avez terminé;)


4
+1, Oui, veuillez lire la variable temporaire pour éviter les E / S de fichier inutiles. C'est une fausse économie que vous sauvegardiez de la mémoire parce que vous avez moins de variables (explicites).
Nick T

2
@NickT: Je m'attendrais à ce qu'un petit fichier lu plusieurs fois soit mis en cache par le système d'exploitation (au moins sous Linux / OSX), donc pas d'E / S de fichier supplémentaire pour une lecture deux fois. Les fichiers volumineux qui ne tiennent pas dans la mémoire ne sont pas mis en cache, mais vous ne voulez pas les lire dans une variable car vous allez commencer à échanger. Donc en cas de doute, lisez toujours plusieurs fois. Si vous savez avec certitude que les fichiers sont petits, faites ce qui vous convient le mieux.
Claude

3
Le démontage peut être automatisé avec with.
Cees Timmerman

30

ouais, comme ci-dessus ...

j'écrirai juste un exemple:

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

17

Tous ceux qui ont répondu à cette question jusqu'à présent ont tout à fait raison - read() parcourent le fichier, donc après l'avoir appelé, vous ne pouvez plus le rappeler.

Ce que j'ajouterai, c'est que dans votre cas particulier, vous n'avez pas besoin de chercher au début ou de rouvrir le fichier, vous pouvez simplement stocker le texte que vous avez lu dans une variable locale et l'utiliser deux fois, ou autant de fois que vous le souhaitez, dans votre programme:

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

1
+1 En fait, c'était la solution proposée pour cet exercice ( code.google.com/intl/de-DE/edu/languages/google-python-class/… ). Mais d'une manière ou d'une autre, je n'ai pas pensé à stocker la chaîne dans une variable. Oh!
helpermethod

1
Avec Python3, utilisez pathlib. from pathlib import Path; text = Path(filename).read_text()Prend soin d'ouvrir, de fermer, etc.
PaulMcG

14

Le pointeur de lecture se déplace après le dernier octet / caractère lu. Utilisez la seek()méthode pour rembobiner le pointeur de lecture au début.


2

Chaque fichier ouvert a une position associée.
Lorsque vous lisez (), vous lisez à partir de cette position. Par exemple, read(10)lit les 10 premiers octets d'un fichier nouvellement ouvert, puis un autre read(10)lit les 10 octets suivants. read()sans arguments lit tout le contenu du fichier, laissant la position du fichier à la fin du fichier. La prochaine fois que vous appelez, read()il n'y a rien à lire.

Vous pouvez utiliser seekpour déplacer la position du fichier. Ou probablement mieux dans votre cas serait d'en faire un read()et de conserver le résultat pour les deux recherches.


1

read() consomme . Ainsi, vous pouvez réinitialiser le fichier ou chercher au début avant de le relire. Ou, si cela correspond à votre tâche, vous pouvez utiliser read(n)pour ne consommer que des noctets.


1

Je trouve toujours que la méthode de lecture ressemble à une promenade dans une ruelle sombre. Vous descendez un peu et vous vous arrêtez mais si vous ne comptez pas vos pas, vous ne savez pas à quel point vous en êtes. Seek donne la solution en repositionnant, l'autre option est Tell qui renvoie la position le long du fichier. Peut-être que l'api du fichier Python peut combiner lecture et recherche dans un read_from (position, octets) pour le rendre plus simple - jusqu'à ce que cela se produise, vous devriez lire cette page .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.