Quelle est la contrepartie parfaite en Python pour «sans être EOF»

115

Pour lire un fichier texte, en C ou en Pascal, j'utilise toujours les extraits de code suivants pour lire les données jusqu'à EOF:

while not eof do begin
  readline(a);
  do_something;
end;

Ainsi, je me demande comment puis-je faire cela simple et rapide en Python?

Allen Koo
la source

Réponses:

192

Faites une boucle sur le fichier pour lire les lignes:

with open('somefile') as openfileobject:
    for line in openfileobject:
        do_something()

Les objets fichier sont itérables et produisent des lignes jusqu'à EOF. L'utilisation de l'objet fichier en tant qu'itérable utilise un tampon pour garantir des lectures performantes.

Vous pouvez faire la même chose avec le stdin (pas besoin d'utiliser raw_input():

import sys

for line in sys.stdin:
    do_something()

Pour compléter l'image, des lectures binaires peuvent être effectuées avec:

from functools import partial

with open('somefile', 'rb') as openfileobject:
    for chunk in iter(partial(openfileobject.read, 1024), b''):
        do_something()

chunkcontiendra jusqu'à 1024 octets à la fois à partir du fichier, et l'itération s'arrête lorsque openfileobject.read(1024)commence à renvoyer des chaînes d'octets vides.

Martijn Pieters
la source
4
Remarque: Le lineaura un nouveau caractère de ligne à la fin.
ben_joseph
1
La lecture de lignes est un peu dangereuse pour les fichiers binaires génériques, parce que peut-être que vous avez une longue ligne de
6GiB
@LtWorf: c'est pourquoi je montre comment lire des fichiers binaires en morceaux plutôt qu'en lignes.
Martijn Pieters
Je lis à partir stdind'un processus en cours d'exécution ... donc il n'a jamais EOF jusqu'à ce que je tue le processus. Mais ensuite, j'atteins la «fin jusqu'à maintenant» et je suis dans l'impasse. Comment puis-je détecter cela et non une impasse? Comme s'il n'y a pas de nouvelles lignes, arrêtez de lire les fichiers (même s'il n'y a pas d'EOF, qui dans mon cas n'existera jamais).
Charlie Parker
@CharlieParker: si vous avez atteint un blocage, alors quelque chose oublie probablement de vider un tampon. Sans un véritable MCVE, il est difficile d'en dire plus.
Martijn Pieters
61

Vous pouvez imiter l'idiome C en Python.

Pour lire un tampon jusqu'à un max_sizenombre d'octets, vous pouvez le faire:

with open(filename, 'rb') as f:
    while True:
        buf = f.read(max_size)
        if not buf:
            break
        process(buf)

Ou, un fichier texte ligne par ligne:

# warning -- not idiomatic Python! See below...
with open(filename, 'rb') as f:
    while True:
        line = f.readline()
        if not line:
            break
        process(line)

Vous devez utiliser while True / breakconstruct car il n'y a pas de test eof en Python autre que le manque d'octets renvoyés par une lecture.

En C, vous pourriez avoir:

while ((ch != '\n') && (ch != EOF)) {
   // read the next ch and add to a buffer
   // ..
}

Cependant, vous ne pouvez pas avoir cela en Python:

 while (line = f.readline()):
     # syntax error

car les affectations ne sont pas autorisées dans les expressions en Python (bien que les versions récentes de Python puissent imiter cela à l'aide d'expressions d'affectation, voir ci-dessous).

Il est certainement plus idiomatique en Python de faire cela:

# THIS IS IDIOMATIC Python. Do this:
with open('somefile') as f:
    for line in f:
        process(line)

Mise à jour: depuis Python 3.8, vous pouvez également utiliser des expressions d'affectation :

 while line := f.readline():
     process(line)
dawg
la source
@MartijnPieters: Maintenant c'est le cas :-)
dawg
3
En tant que programmeur C et Perl, votre argument selon lequel les affectations ne sont pas autorisées dans les expressions a été crucial pour moi.
CODE-REaD
1
La méthode "while True:" est également utile lorsque vous devez opérer sur plus d'une ligne d'entrée par itération, ce que le Python idiomatique ne permet pas (pour autant que je sache, en tout cas).
Donald Smith
Vous ne devriez pas lire de lignes si vous ne faites pas d'hypothèses sur le fichier. Un fichier binaire peut avoir d'énormes lignes…
LtWorf
Il semble qu'il y ait un avantage à la readline()méthode non idiomatique : vous pouvez effectuer une gestion des erreurs fine, comme la capture UnicodeDecodeError, ce que vous ne pouvez pas faire avec l' foritération idiomatique .
flow2k
17

L'idiome Python pour ouvrir un fichier et le lire ligne par ligne est:

with open('filename') as f:
    for line in f:
        do_something(line)

Le fichier sera automatiquement fermé à la fin du code ci-dessus (la withconstruction s'en charge).

Enfin, il convient de noter que linecela conservera la nouvelle ligne de fin. Cela peut être facilement supprimé en utilisant:

line = line.rstrip()
NPE
la source
1
+1, soulignant également au PO que ce n'est pas la même chose que la for line in f.readlines(): ...solution très similaire , couramment suggérée.
jedwards
12

Vous pouvez utiliser l'extrait de code ci-dessous pour lire ligne par ligne, jusqu'à la fin du fichier

line = obj.readline()
while(line != ''):

    # Do Something

    line = obj.readline()
AR
la source
1
OMI, c'est la seule réponse qui reflète le mieux ce qui a été demandé.
gvrocha
Souvent, itérer sur les lignes déformerait la structure du programme. Par exemple, dans un analyseur de langage, vous souhaitez lire les lignes et les traiter en séquence. Vous ne voulez pas restructurer le niveau supérieur juste pour pouvoir lire des lignes en boucle, puis les envoyer à l'analyseur.
Jonathan Starr
11

Bien qu'il y ait des suggestions ci-dessus pour "le faire à la manière python", si l'on veut vraiment avoir une logique basée sur EOF, alors je suppose que l'utilisation de la gestion des exceptions est le moyen de le faire -

try:
    line = raw_input()
    ... whatever needs to be done incase of no EOF ...
except EOFError:
    ... whatever needs to be done incase of EOF ...

Exemple:

$ echo test | python -c "while True: print raw_input()"
test
Traceback (most recent call last):
  File "<string>", line 1, in <module> 
EOFError: EOF when reading a line

Ou appuyez sur Ctrl-Zà une raw_input()invite (Windows, Ctrl-ZLinux)

user5472996
la source
@TessellatingHeckler ce n'est pas ce que dit la documentation : "Déclenché lorsque l'une des fonctions intégrées (input () ou raw_input ()) atteint une condition de fin de fichier (EOF) sans lire aucune donnée."
Tadhg McDonald-Jensen
1
@ TadhgMcDonald-Jensen Eh bien, il en sera ainsi. Tellement bizzare. La fausse réclamation a été retirée et le vote défavorable injuste supprimé.
TessellatingHeckler
1

Vous pouvez utiliser l'extrait de code suivant. readlines () lit le fichier entier à la fois et le divise par ligne.

line = obj.readlines()
Aditeya Pandey
la source
0

En plus de la bonne réponse de @ dawg, la solution équivalente utilisant l'opérateur morse (Python> = 3,8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
Infini
la source