Jaki jest idealny odpowiednik w Pythonie dla „while not EOF”

115

Aby przeczytać jakiś plik tekstowy, w C lub Pascalu, zawsze używam następujących fragmentów do odczytu danych do EOF:

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

Dlatego zastanawiam się, jak mogę zrobić to prosto i szybko w Pythonie?

Allen Koo
źródło

Odpowiedzi:

192

Zapętl plik, aby odczytać linie:

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

Obiekty plików są iterowalne i dają linie do EOF. Użycie obiektu pliku jako elementu iteracyjnego wykorzystuje bufor, aby zapewnić wydajne odczyty.

Możesz zrobić to samo ze stdin (nie musisz używać raw_input():

import sys

for line in sys.stdin:
    do_something()

Aby uzupełnić obraz, odczyty binarne można wykonać za pomocą:

from functools import partial

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

gdzie chunkbędzie zawierał do 1024 bajtów naraz z pliku, a iteracja zostanie zatrzymana, gdy openfileobject.read(1024)zacznie zwracać puste ciągi bajtów.

Martijn Pieters
źródło
4
Uwaga: Na linekońcu pojawi się znak nowej linii.
ben_joseph,
1
Czytanie wierszy jest nieco niebezpieczne dla ogólnych plików binarnych, ponieważ być może masz długą linię
6GiB
@LtWorf: dlatego pokazuję, jak czytać pliki binarne w kawałkach, a nie w wierszach.
Martijn Pieters
Czytam z stdinuruchomionego procesu ... więc nigdy nie ma EOF, dopóki nie zabiję procesu. Ale potem dochodzę do „końca do teraz” i wpadam w impas. Jak to wykryć, a nie zakleszczenie? Na przykład, jeśli nie ma nowych linii, przestań czytać pliki (nawet jeśli nie ma EOF, który w moim przypadku nigdy nie będzie istniał).
Charlie Parker
@CharlieParker: jeśli doszło do impasu, prawdopodobnie coś zapomina o opróżnieniu bufora. Bez faktycznego MCVE trudno powiedzieć coś więcej.
Martijn Pieters
61

Możesz naśladować idiom C w Pythonie.

Aby odczytać bufor o maksymalnej max_sizeliczbie bajtów, możesz to zrobić:

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

Lub plik tekstowy wiersz po wierszu:

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

Musisz użyć while True / breakkonstrukcji, ponieważ w Pythonie nie ma testu eof poza brakiem bajtów zwróconych z odczytu.

W C możesz mieć:

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

Jednak nie możesz tego mieć w Pythonie:

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

ponieważ przypisania nie są dozwolone w wyrażeniach w Pythonie (chociaż najnowsze wersje Pythona mogą to naśladować za pomocą wyrażeń przypisania, patrz poniżej).

Z pewnością w Pythonie jest to bardziej idiomatyczne:

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

Aktualizacja: od Pythona 3.8 możesz także używać wyrażeń przypisania :

 while line := f.readline():
     process(line)
psie
źródło
@MartijnPieters: Teraz to robi :-)
dawg
3
Jako programista C i Perl, twoja uwaga, że przypisania nie są dozwolone w wyrażeniach, była dla mnie kluczowa.
CODE-REaD
1
Metoda „while True:” jest również przydatna, gdy trzeba operować na więcej niż jednym wierszu wejściowym na iterację, na co idiomatyczny Python nie pozwala (o ile wiem, w każdym razie).
Donald Smith
Nie powinieneś czytać wierszy, jeśli nie robisz założeń w pliku. Plik binarny może mieć ogromne linie…
LtWorf
Wydaje się, że sposób nie idiomatyczny ma pewną zaletę readline(): możesz wykonać drobnoziarnistą obsługę błędów, taką jak wyłapywanie UnicodeDecodeError, czego nie można zrobić z idiomatyczną foriteracją.
flow2k
17

Idiom Pythona do otwierania pliku i czytania go wiersz po wierszu to:

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

Plik zostanie automatycznie zamknięty na końcu powyższego kodu ( withzajmuje się tym konstrukcja).

Na koniec warto zauważyć, że linezachowa końcowy znak nowej linii. Można to łatwo usunąć za pomocą:

line = line.rstrip()
NPE
źródło
1
+1, wskazując również PO, że nie jest to to samo, co bardzo podobne for line in f.readlines(): ..., powszechnie sugerowane rozwiązanie.
jedwards
12

Możesz użyć poniższego fragmentu kodu, aby czytać wiersz po wierszu, aż do końca pliku

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

    # Do Something

    line = obj.readline()
AR
źródło
1
IMO, to jest jedyna odpowiedź, która najlepiej odzwierciedla to, o co pytano.
gvrocha
Często powtarzanie po liniach zniekształcało strukturę programu. Na przykład w parserze języka chcesz czytać wiersze i przetwarzać je po kolei. Nie chcesz zmieniać struktury najwyższego poziomu tylko po to, abyś mógł odczytać wiersze w pętli, a następnie wysłać je do parsera.
Jonathan Starr
11

Chociaż powyżej są sugestie dotyczące "robienia tego w Pythonie", jeśli naprawdę chce się mieć logikę opartą na EOF, przypuszczam, że użycie obsługi wyjątków jest sposobem na zrobienie tego -

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

Przykład:

$ 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

Lub naciśnij Ctrl-Zpo wyświetleniu raw_input()monitu (Windows, Ctrl-ZLinux)

user5472996
źródło
@TessellatingHeckler to nie jest to, co mówi dokumentacja : „ Wywoływane, gdy jedna z wbudowanych funkcji (input () lub raw_input ()) osiąga warunek końca pliku (EOF) bez odczytywania żadnych danych”.
Tadhg McDonald-Jensen
1
@ TadhgMcDonald-Jensen Cóż, hej, więc będzie. Jakie to dziwne. Fałszywe roszczenie zostało wycofane, a niesprawiedliwy głos przeciw został usunięty.
TessellatingHeckler
1

Możesz użyć następującego fragmentu kodu. readlines () czyta cały plik naraz i dzieli go liniami.

line = obj.readlines()
Aditeya Pandey
źródło
0

Oprócz świetnej odpowiedzi @ dawg, równoważne rozwiązanie wykorzystujące operator morsa (Python> = 3,8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
Nieskończoność
źródło