2009-11-17 39 views
9

Tôi cần phải lặp lại cho đến khi tôi nhấn vào cuối của một đối tượng giống như tập tin, nhưng tôi không tìm thấy một "cách rõ ràng để làm điều đó", mà làm cho tôi nghi ngờ tôi nhìn thấy một cái gì đó, tốt, rõ ràng. :-)Làm thế nào để lặp lại cho đến khi EOF bằng Python?

Tôi có một dòng (trong trường hợp này, đó là một đối tượng StringIO, nhưng tôi tò mò về những trường hợp chung cũng) mà các cửa hàng một số không rõ các bản ghi trong "< length> < dữ liệu>" định dạng , ví dụ:

data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00") 

Bây giờ, cách duy nhất rõ ràng tôi có thể tưởng tượng để đọc đang sử dụng (những gì tôi nghĩ là) một vòng lặp khởi tạo, mà dường như một chút un-Pythonic:

len_name = data.read(4) 

while len_name != "": 
    len_name = struct.unpack("<I", len_name)[0] 
    names.append(data.read(len_name)) 

    len_name = data.read(4) 

Trong ngôn ngữ giống như C, tôi chỉ dán read(4) vào 012 Điều khoản thử nghiệm của, nhưng tất nhiên điều đó sẽ không hoạt động đối với Python. Bất kỳ suy nghĩ về một cách tốt hơn để thực hiện điều này?

+0

Vì vậy, nhiều câu trả lời! Có lẽ tôi không nên đăng ngay trước khi đi ăn trưa. :-) –

Trả lời

24

Bạn có thể kết hợp lặp qua iter() với một trọng điểm:

for block in iter(lambda: file_obj.read(4), ""): 
    use(block) 
+1

Chắc chắn là anwser tốt nhất. Bạn đã cho tôi trên này, tôi quên điều này rất hữu ích sentinel. –

+0

Tôi nghĩ tôi cũng thích cái này nhất; những gì nó làm là rất rõ ràng vì có quá ít mã. Cảm ơn đã giúp đỡ! –

+1

Chỉ cần nhắc nhở để loại và tua lại tập tin nếu bạn đã viết cho nó, trước khi lặp qua nó, tức là file_obj.seek (0). – mikemaccana

10

Bạn đã thấy cách lặp qua các dòng trong tệp văn bản chưa?

for line in file_obj: 
    use(line) 

Bạn có thể làm điều tương tự với máy phát điện riêng bạn:

def read_blocks(file_obj, size): 
    while True: 
    data = file_obj.read(size) 
    if not data: 
     break 
    yield data 

for block in read_blocks(file_obj, 4): 
    use(block) 

Xem thêm:

+0

Bạn cũng có thể cấu trúc vòng lặp của mình dưới dạng vòng lặp while trong trình tạo. Sử dụng bất cứ điều gì là dễ đọc nhất. –

3

tôi nhìn thấy, như dự đoán, mà điển hình và câu trả lời phổ biến nhất là sử dụng các trình tạo rất chuyên dụng để "đọc 4 byte tại một thời gian". Đôi khi tính tổng quát không phải là bất kỳ khó khăn hơn (và bổ ích ;-) nhiều hơn nữa, vì vậy, tôi đã đề nghị thay vì giải pháp rất chung sau đây:

import operator 
def funlooper(afun, *a, **k): 
    wearedone = k.pop('wearedone', operator.not_) 
    while True: 
    data = afun(*a, **k) 
    if wearedone(data): break 
    yield data 

Bây giờ đầu vòng lặp mong muốn của bạn chỉ là: for len_name in funlooper(data.read, 4):.

Sửa: làm nhiều hơn nữa chung bởi wearedone thành ngữ từ một lời nhận xét cáo buộc phiên bản hơi ít nói chung trước đây của tôi (thể xác định rõ kiểm tra lối ra như if not data:) của việc có "một sự phụ thuộc ẩn", của tất cả mọi thứ -)

!

Trường hợp phổ biến dao quân đội swiss các vòng lặp, itertools, là tốt quá, tất nhiên, như thường lệ:

import itertools as it 

for len_name in it.takewhile(bool, it.imap(data.read, it.repeat(4))): ... 

hay, khá tương đương:

import itertools as it 

def loop(pred, fun, *args): 
    return it.takewhile(pred, it.starmap(fun, it.repeat(args))) 

for len_name in loop(bool, data.read, 4): ... 
+0

Mặc dù có sự phụ thuộc ẩn, vì funlooper yêu cầu hàm trả về kết quả không đúng để biểu thị kết thúc. –

+0

@ R.Pate, tất nhiên bạn có thể thêm một cách hợp lý vào funlooper một đối số vị ngữ 'wearedone' mặc định là' operator.not_' và thay đổi 'if' thành' if wearedone (data): break' - tôi đã không nghĩ rằng nó có giá trị hơn nữa generalizing câu trả lời với mã tầm thường này khi tôi đã chắc chắn (và chính xác trong đó ;-) rằng các câu trả lời khác sẽ được WAY vượt trội chuyên ngành (không có lợi ích). Ah tốt, kể từ khi chuyên môn quá mức là chiến thắng trong ngày anyway, hãy để tôi chỉnh sửa câu trả lời để cho thấy rằng tổng quát không phải là bất kỳ khó khăn hơn (và nhiều hơn nữa bổ ích) trong trường hợp này ;-). –

+0

Tôi nghĩ rằng bạn đã hiểu sai tôi theo hướng sai: IMHO bản funlooper ban đầu là * quá * chung. Vì chúng ta đã phụ thuộc vào giá trị trả về có một biểu mẫu cụ thể, nên nó hợp lý ở đây để phụ thuộc vào phần này của giao diện giống như tệp (phương thức đọc), thay vì cố gắng chuyển một kiểu gọi chung. Nếu không, người dùng ít nhất phải nhận thức được sự phụ thuộc. –

1

Điểm đánh dấu EOF trong python là một chuỗi rỗng vì vậy những gì bạn có là khá gần với tốt nhất bạn sẽ nhận được mà không cần viết một chức năng để bọc này lên trong một iterator. Tôi có thể được viết bằng một chút cách pythonic hơn bằng cách thay đổi while như:

while len_name: 
    len_name = struct.unpack("<I", len_name)[0] 
    names.append(data.read(len_name)) 
    len_name = data.read(4) 
+2

Điều này đòi hỏi phải nhân đôi việc gán cho len_name trước vòng lặp (mà bạn đã bỏ qua), và nó gần như luôn luôn muốn tránh trùng lặp này. –

5

Tôi thích giải pháp lặp dựa trên đã đề cập đến biến này vào một vòng lặp for.Một giải pháp khác được viết trực tiếp là "loop-and-a-half" của Knuth là

while 1: 
    len_name = data.read(4) 
    if not len_name: 
     break 
    names.append(data.read(len_name)) 

Bạn có thể thấy bằng cách so sánh cách dễ dàng lắp vào máy phát riêng của nó và được sử dụng làm vòng lặp.

+0

Trong trường hợp đặc biệt này, tôi nghĩ rằng tôi thích giải pháp 'iter()' tốt hơn, nhưng tôi cảm thấy khá ngu ngốc khi không nghĩ về điều này. Một +1 xứng đáng cho bạn. ;-) –

+0

Wow. Yeah, đó là giải pháp iter() là tốt đẹp. Kết hợp với một "lambda:" và tùy thuộc vào đóng cửa làm cho nó một chút khó hiểu, nhưng ngọt không-ít hơn. –

0

Tôi muốn đi với đề nghị Tendayi của tái chức năng và iterator để có thể đọc:

def read4(): 
    len_name = data.read(4) 
    if len_name: 
     len_name = struct.unpack("<I", len_name)[0] 
     return data.read(len_name) 
    else: 
     raise StopIteration 

for d in iter(read4, ''): 
    names.append(d) 
+0

Không có lý do gì, chỉ cần một cái gì đó tôi đặt lại với nhau một cách nhanh chóng. Tôi đã sửa đổi đoạn mã. –

Các vấn đề liên quan