Đối tác hoàn hảo trong Python là gì cho "trong khi không phải EOF"


114

Để đọc một số tệp văn bản, trong C hoặc Pascal, tôi luôn sử dụng các đoạn mã sau để đọc dữ liệu cho đến khi EOF:

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

Vì vậy, tôi tự hỏi làm thế nào tôi có thể thực hiện việc này đơn giản và nhanh chóng trong Python?

Câu trả lời:


189

Lặp lại tệp để đọc các dòng:

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

Các đối tượng tệp có thể lặp lại và các dòng cho đến khi EOF. Việc sử dụng đối tượng tệp như một tệp có thể lặp lại sử dụng bộ đệm để đảm bảo các lần đọc hiệu suất.

Bạn có thể làm tương tự với stdin (không cần sử dụng raw_input():

import sys

for line in sys.stdin:
    do_something()

Để hoàn thành bức tranh, có thể thực hiện đọc nhị phân với:

from functools import partial

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

nơi chunksẽ chứa tối đa 1024 byte tại một thời điểm từ tệp và quá trình lặp dừng khi openfileobject.read(1024)bắt đầu trả về chuỗi byte trống.


4
Lưu ý: Di linechúc có một ký tự dòng mới ở cuối.
ben_joseph

1
Đọc dòng là một chút nguy hiểm cho các tập tin nhị phân chung chung, vì có thể bạn có một đường dài 6GiB ...
LtWorf

@LtWorf: đó là lý do tại sao tôi chỉ cách đọc các tệp nhị phân theo khối chứ không phải theo dòng.
Martijn Pieters

Tôi đang đọc từ một stdinquá trình đang chạy ... vì vậy nó không bao giờ có EOF cho đến khi tôi giết quá trình. Nhưng sau đó tôi đạt đến "cuối cùng cho đến nay" và tôi bế tắc. Làm cách nào để phát hiện ra điều này và không bị bế tắc? Giống như nếu không có dòng mới, hãy ngừng đọc tệp (ngay cả khi không có EOF, trong trường hợp của tôi sẽ không bao giờ tồn tại).
Charlie Parker

@CharlieParker: nếu bạn đã gặp bế tắc, thì có thể là do bạn quên làm sạch bộ đệm. Nếu không có MCVE thực tế, khó có thể nói gì hơn thế.
Martijn Pieters

61

Bạn có thể bắt chước thành ngữ C trong Python.

Để đọc một bộ đệm lên đến max_sizesố byte, bạn có thể làm như sau:

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

Hoặc, một tệp văn bản từng dòng:

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

Bạn cần sử dụng while True / breakcấu trúc vì không có kiểm tra eof nào trong Python ngoài việc thiếu byte trả về từ một lần đọc.

Trong C, bạn có thể có:

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

Tuy nhiên, bạn không thể có điều này trong Python:

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

bởi vì phép gán không được phép trong biểu thức trong Python (mặc dù các phiên bản Python gần đây có thể bắt chước điều này bằng cách sử dụng các biểu thức gán, xem bên dưới).

Nó chắc chắn là thành ngữ hơn trong Python để làm điều này:

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

Cập nhật: Kể từ Python 3.8, bạn cũng có thể sử dụng các biểu thức gán :

 while line := f.readline():
     process(line)

@MartijnPieters: Bây giờ thì có :-)
dawg

3
Là một lập trình viên C và Perl, quan điểm của bạn rằng các phép gán không được phép trong các biểu thức là rất quan trọng đối với tôi.
CODE-REaD

1
Phương thức "while True:" cũng hữu ích khi bạn cần thao tác trên nhiều dòng đầu vào mỗi lần lặp, điều mà Python thành ngữ không cho phép (dù sao thì theo như tôi có thể nói).
Donald Smith,

Bạn không nên đọc các dòng nếu bạn không đưa ra các giả định trong tệp. Một tập tin nhị phân có thể có những dòng rất lớn ...
LtWorf

Có vẻ như có một lợi thế đối với cách không thành ngữ readline(): bạn có thể xử lý lỗi chi tiết, chẳng hạn như bắt UnicodeDecodeError, điều mà bạn không thể làm với forlặp thành ngữ .
flow2k

17

Thành ngữ Python để mở tệp và đọc từng dòng là:

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

Tệp sẽ tự động được đóng ở cuối đoạn mã trên ( with cấu trúc sẽ xử lý việc đó).

Cuối cùng, điều đáng chú ý là linesẽ bảo toàn dòng mới theo sau. Điều này có thể dễ dàng loại bỏ bằng cách sử dụng:

line = line.rstrip()

1
+1, cũng chỉ ra cho OP rằng điều này không giống với for line in f.readlines(): ...giải pháp tương tự , một giải pháp thường được đề xuất.
jedwards

12

Bạn có thể sử dụng đoạn mã bên dưới để đọc từng dòng cho đến cuối tệp

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

    # Do Something

    line = obj.readline()

1
IMO, đây là một câu trả lời phản ánh tốt nhất những gì được hỏi.
gvrocha

Thường thì việc lặp lại các dòng sẽ làm sai lệch cấu trúc của chương trình. Ví dụ: trong trình phân tích cú pháp ngôn ngữ, bạn muốn đọc các dòng và xử lý chúng theo trình tự. Bạn không muốn cơ cấu lại cấp cao nhất để có thể lặp lại các dòng đọc và sau đó gửi chúng đến trình phân tích cú pháp.
Jonathan Starr

11

Mặc dù có các đề xuất ở trên về "làm theo cách python", nhưng nếu ai đó muốn thực sự có logic dựa trên EOF, thì tôi cho rằng sử dụng xử lý ngoại lệ là cách để làm điều đó -

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

Thí dụ:

$ 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

Hoặc nhấn Ctrl-Zvào raw_input()lời nhắc (Windows, Ctrl-ZLinux)


@TessellatingHeckler không giống như tài liệu nói: "Tăng lên khi một trong các hàm tích hợp (input () hoặc raw_input ()) gặp điều kiện cuối tệp (EOF) mà không đọc bất kỳ dữ liệu nào."
Tadhg McDonald-Jensen

1
@ TadhgMcDonald-Jensen Chà, thế là xong. Thật kỳ lạ. Đã rút lại khiếu nại sai và xóa phiếu phản đối không công bằng.
TessellatingHeckler

1

Bạn có thể sử dụng đoạn mã sau. readlines () đọc toàn bộ tệp cùng một lúc và tách nó ra từng dòng.

line = obj.readlines()

0

Ngoài câu trả lời tuyệt vời của @ dawg, giải pháp tương đương sử dụng toán tử hải mã (Python> = 3.8):

with open(filename, 'rb') as f:
    while buf := f.read(max_size):
        process(buf)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.