2010-10-11 17 views
56

Pour un exercice que je fais, j'essaie de lire deux fois le contenu d'un fichier donné en utilisant la méthode read(). Curieusement, quand je l'appelle la deuxième fois, il ne semble pas retourner le contenu du fichier sous forme de chaîne?Pourquoi ne puis-je pas appeler read() deux fois sur un fichier ouvert?

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 plus efficace ou mieux, ce n'est pas le point ici. Le point est, pourquoi ne puis-je pas appeler read() deux fois? Dois-je réinitialiser la poignée de fichier? Ou fermer/rouvrir le fichier pour le faire?

+2

Où avez-vous eu l'idée que lire ne changerait pas l'état du fichier? Quelle référence ou tutoriel utilisez-vous? –

+0

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

+0

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

Répondre

88

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

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

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

+3

+1, Oui, s'il vous plaît lisez la variable temporaire pour évitez les E/S de fichiers inutiles. C'est une fausse économie que vous économisiez de la mémoire parce que vous avez moins de variables (explicites). –

+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 sur Linux/OSX), donc pas d'E/S de fichier supplémentaire pour lire 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 à les échanger. Donc en cas de doute, lisez toujours plusieurs fois. Si vous savez avec certitude que les fichiers sont petits, faites ce que vous voulez. – Claude

+0

Déchirer peut être automatisé avec ['with'] (http://effbot.org/zone/python-with-statement.htm). –

13

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

2

Chaque fichier ouvert à 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 seek pour déplacer la position du fichier. Ou probablement mieux dans votre cas serait de faire un read() et de garder le résultat pour les deux recherches.

13

Tous ceux qui ont déjà répondu à cette question ont absolument raison - read() se déplace dans le fichier, donc après l'avoir appelé, vous ne pouvez pas l'appeler à nouveau. Ce que j'ajouterai est que dans votre cas particulier, vous n'avez pas besoin de revenir au début ou de rouvrir le fichier, vous pouvez juste stocker le texte que vous avez lu dans une variable locale, et 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 (http://code.google.com/intl/de-DE/edu/languages/google-python-class/exercises/baby-names.html). Mais de toute façon je n'ai pas pensé à stocker la chaîne dans une variable. D'oh! – helpermethod

+1

Avec Python3, utilisez pathlib. 'depuis Pathlib Import Path; text = Chemin (filename) .read_text() 'Prend soin d'ouvrir, de fermer, etc. – PaulMcG

1

read() consume. Donc, vous pouvez réinitialiser le fichier, ou chercher au début avant de relire. Ou, si elle se termine votre tâche, vous pouvez utiliser read(n) pour consommer seulement n octets.

12

ouais, comme ci-dessus ...

je vais écrire juste un exemple:

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

Je trouve toujours la méthode de lecture quelque chose d'une promenade dans une ruelle sombre. Vous descendez un peu et vous arrêtez mais si vous ne comptez pas vos pas, vous n'êtes pas sûr de la distance qui vous sépare. La recherche donne la solution en repositionnant, l'autre option est Tell qui retourne la position le long du fichier. Peut être le fichier Python api peut combiner lire et chercher dans un read_from (position, octets) pour le rendre plus simple - jusqu'à ce que cela se produise, vous devriez lire this page.