อะไรคือคู่ที่สมบูรณ์แบบใน Python สำหรับ“ ขณะที่ไม่ใช่ EOF”


115

ในการอ่านไฟล์ข้อความใน C หรือ Pascal ฉันมักจะใช้ตัวอย่างต่อไปนี้เพื่ออ่านข้อมูลจนถึง EOF:

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

ดังนั้นฉันสงสัยว่าฉันจะทำสิ่งนี้ให้ง่ายและรวดเร็วใน Python ได้อย่างไร

คำตอบ:


192

วนซ้ำไฟล์เพื่ออ่านบรรทัด:

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

อ็อบเจ็กต์ไฟล์สามารถทำซ้ำได้และให้ผลบรรทัดจนถึง EOF การใช้อ็อบเจ็กต์ไฟล์เป็นตัวทำซ้ำใช้บัฟเฟอร์เพื่อให้แน่ใจว่าตัวดำเนินการอ่าน

คุณสามารถทำเช่นเดียวกันกับ stdin (ไม่จำเป็นต้องใช้raw_input():

import sys

for line in sys.stdin:
    do_something()

เพื่อให้ภาพสมบูรณ์การอ่านไบนารีสามารถทำได้ด้วย:

from functools import partial

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

โดยที่ไฟล์chunkจะมีได้ถึง 1024 ไบต์ต่อครั้งและการวนซ้ำจะหยุดลงเมื่อopenfileobject.read(1024)เริ่มส่งคืนสตริงไบต์ว่าง


4
หมายเหตุ: lineจะมีอักขระบรรทัดใหม่ต่อท้าย
ben_joseph

1
การอ่านบรรทัดเป็นเรื่องที่อันตรายสำหรับไฟล์ไบนารีทั่วไปเพราะบางทีคุณอาจมีบรรทัดยาว 6GiB …
LtWorf

@LtWorf: นั่นคือเหตุผลที่ฉันแสดงวิธีอ่านไฟล์ไบนารีเป็นชิ้น ๆแทนที่จะเป็นบรรทัด
Martijn Pieters

ฉันกำลังอ่านจาก a stdinจากกระบวนการทำงาน ... ดังนั้นจึงไม่มี EOF จนกว่าฉันจะฆ่ากระบวนการ แต่แล้วฉันก็มาถึง "ถึงตอนนี้" และฉันก็หยุดชะงัก ฉันจะตรวจจับสิ่งนี้และไม่หยุดชะงักได้อย่างไร เช่นถ้าไม่มีบรรทัดใหม่ให้หยุดอ่านไฟล์ (แม้ว่าจะไม่มี EOF ซึ่งในกรณีของฉันจะไม่มีอยู่)
Charlie Parker

@CharlieParker: ถ้าคุณมาถึงทางตันอาจมีบางอย่างลืมที่จะล้างบัฟเฟอร์ หากไม่มี MCVE จริงก็ยากที่จะพูดอะไรมากไปกว่านั้น
Martijn Pieters

61

คุณสามารถเลียนแบบ C idiom ใน Python

หากต้องการอ่านบัฟเฟอร์ถึงmax_sizeจำนวนไบต์คุณสามารถทำได้:

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

หรือไฟล์ข้อความทีละบรรทัด:

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

คุณต้องใช้การwhile True / breakสร้างเนื่องจากไม่มีการทดสอบ eofใน Python นอกเหนือจากการขาดไบต์ที่ส่งคืนจากการอ่าน

ใน C คุณอาจมี:

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

อย่างไรก็ตามคุณไม่สามารถมีได้ใน Python:

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

เนื่องจากไม่อนุญาตให้มีการมอบหมายงานในนิพจน์ใน Python (แม้ว่า Python เวอร์ชันล่าสุดสามารถเลียนแบบสิ่งนี้ได้โดยใช้นิพจน์การกำหนดดูด้านล่าง)

มันเป็นสำนวนมากกว่าใน Python ที่จะทำสิ่งนี้:

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

อัปเดต:เนื่องจาก Python 3.8 คุณสามารถใช้นิพจน์การกำหนดได้ :

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

@MartijnPieters: ตอนนี้มัน :-)
dawg

3
ในฐานะโปรแกรมเมอร์ C และ Perl ประเด็นของคุณที่ไม่อนุญาตให้มีการมอบหมายงานในนิพจน์เป็นสิ่งสำคัญสำหรับฉัน
CODE-REaD

1
เมธอด "while True:" ยังมีประโยชน์เมื่อคุณต้องดำเนินการกับบรรทัดอินพุตมากกว่าหนึ่งบรรทัดต่อการวนซ้ำซึ่งเป็นสิ่งที่ Python สำนวนไม่อนุญาต (เท่าที่ฉันสามารถบอกได้)
Donald Smith

คุณไม่ควรอ่านบรรทัดหากคุณไม่ตั้งสมมติฐานในไฟล์ ไฟล์ไบนารีอาจมีเส้นขนาดใหญ่…
LtWorf

ดูเหมือนว่าจะมีข้อได้เปรียบสำหรับวิธีที่ไม่ใช้สำนวนนั่นคือreadline()คุณสามารถจัดการข้อผิดพลาดแบบละเอียดได้เช่นการจับUnicodeDecodeErrorซึ่งคุณไม่สามารถทำได้ด้วยการforทำซ้ำสำนวน
flow2k

17

สำนวน Python สำหรับเปิดไฟล์และอ่านทีละบรรทัดคือ:

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

ไฟล์จะถูกปิดโดยอัตโนมัติในตอนท้ายของโค้ดด้านบน ( withโครงสร้างจะดูแลสิ่งนั้น)

สุดท้ายเป็นที่น่าสังเกตว่าlineจะรักษาบรรทัดใหม่ต่อท้าย สามารถลบออกได้อย่างง่ายดายโดยใช้:

line = line.rstrip()

1
+1 ยังชี้ให้ OP เห็นว่านี่ไม่เหมือนกับfor line in f.readlines(): ...โซลูชันที่คล้ายกันซึ่งเป็นวิธีการแก้ปัญหาที่แนะนำโดยทั่วไป
jedwards

12

คุณสามารถใช้ข้อมูลโค้ดด้านล่างเพื่ออ่านทีละบรรทัดจนจบไฟล์

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

    # Do Something

    line = obj.readline()

1
IMO นี่คือคำตอบเดียวที่สะท้อนสิ่งที่ถามได้ดีที่สุด
gvrocha

บ่อยครั้งที่การทำซ้ำหลายบรรทัดจะทำให้โครงสร้างของโปรแกรมผิดเพี้ยนไป ตัวอย่างเช่นในโปรแกรมแยกวิเคราะห์ภาษาคุณต้องการอ่านบรรทัดและประมวลผลตามลำดับ คุณไม่ต้องการปรับโครงสร้างระดับบนสุดเพียงเพื่อให้คุณสามารถวนซ้ำบรรทัดการอ่านแล้วส่งไปยังโปรแกรมแยกวิเคราะห์
Jonathan Starr

11

ในขณะที่มีคำแนะนำข้างต้นสำหรับ "ทำแบบ python" แต่ถ้าใครอยากมีตรรกะตาม EOF จริงๆฉันคิดว่าการใช้การจัดการข้อยกเว้นเป็นวิธีที่ต้องทำ -

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

ตัวอย่าง:

$ 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

หรือกดCtrl-Zที่raw_input()พรอมต์ (Windows, Ctrl-ZLinux)


@TessellatingHeckler นั่นไม่ใช่สิ่งที่เอกสารระบุว่า: "เพิ่มขึ้นเมื่อหนึ่งในฟังก์ชันในตัว (input () หรือ raw_input ()) กระทบกับ end-of-file condition (EOF) โดยไม่อ่านข้อมูลใด ๆ "
Tadhg McDonald-Jensen

1
@ TadhgMcDonald-Jensen เฮ้มันจะเป็นเช่นนั้น แปลกแค่ไหน การอ้างสิทธิ์ที่เป็นเท็จถูกเพิกถอนและลบการลงคะแนนที่ไม่เป็นธรรม
TessellatingHeckler

1

คุณสามารถใช้ข้อมูลโค้ดต่อไปนี้ readlines () อ่านทั้งไฟล์พร้อมกันและแยกทีละบรรทัด

line = obj.readlines()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.