2009-11-17 25 views
9

Dosya benzeri bir nesnenin sonuna gelene kadar dönmem gerekiyor, ancak "bunu yapmanın bariz bir yolu" bulamıyorum, bu da bana bir şeyle baktığımdan şüphelenmemi sağlıyor. :-)Python'da EOF'ye nasıl geçilir?

Bir akışım var (bu durumda, bu bir StringIO nesnesi, ama genel durum hakkında da merak ediyorum) "< uzunluğu> < veri>" biçiminde bilinmeyen sayıda kayıt tutuyor örneğin: Şimdi

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

, bu (Ben ne düşündüğünü) biraz un-pythonic görünüyor, başlatılan bir döngü kullanmaktadır okumayı hayal tek net yol:

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) 

C benzeri bir dilde, 012 read(4)'u 012'ye yapıştıracağım's test cümlesi, ama tabii ki bu Python için çalışmayacak. Bunu başarmanın daha iyi bir yolu üzerinde herhangi bir düşünce?

+0

kadar çok cevap! Belki de öğle yemeğine gitmeden hemen önce göndermemeliydim. :-) –

cevap

24

Bir nöbetçi ile iter() yoluyla yineleme birleştirebilirsiniz:

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

Kesinlikle en iyi anwser. Beni bu konuda aldın, bunu çok yararlı bir şekilde unuttum. –

+0

Sanırım bunu en çok beğendim; Yaptığı şey çok açık çünkü çok az kod var. Yardım için teşekkürler! –

+1

Dosyaya yazdıysanız, dosya üzerinde yineleme yapmadan önce, örneğin file_obj.seek (0) gibi bir hatırlatma yapınız. – mikemaccana

10

Metin dosyasında satırların üzerinden nasıl yinelendiğini gördünüz mü?

for line in file_obj: 
    use(line) 

Kendi jeneratör ile aynı şeyi yapabilirsiniz:

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) 

Ayrıca bkz:

    Tahmin ettiğim gibi, bakınız tipik olduğunu
  • file.read
+0

Ayrıca, döngünüzü jeneratördeki while döngüsü olarak da yapılandırabilirsiniz. En okunaklı olanı kullanın. –

3

ve en popüler cevap, "bir bayta 4 bayt okuma" için çok özel jeneratörler kullanıyor zaman". Bazen genelliği yerine aşağıdaki çok genel bir çözüm öneri olarak sunduk herhangi sert (ve çok daha ;-) ödüllendirici değil, yani:

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 

Şimdi istediğiniz döngü başlığı adildir: for len_name in funlooper(data.read, 4):.

Düzenleme: Yorum herşeyin "gizli bir bağımlılık", sahip (if not data: olarak çıkış testi hardcoding) benim biraz daha az genel önceki sürümü sanık beri wearedone deyim tarafından çok daha genel yapılmış -)

!

, itertools döngü olağan İsviçre çakısı, her zamanki gibi, tabii ki, çok ince: oldukça eşdeğer

import itertools as it 

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

ya,:

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

Gizli bir bağımlılık olmasına rağmen, funlooper, sonlanmaması için gerçek olmayan bir sonuç döndürmesi işlevini gerektirir. Sadece vermedi - break': –

+0

R.Pate @, sen elbette trivially operator.not_' 'varsaymak bir' wearedone' yüklem argüman funlooper ve 'etmek if'' wearedone (veri) eğer değiştirmek ekleyebilir Bu önemsiz kodla cevabı genellemeye değdiğine inanıyorum (ve eminim ki ;-); diğer cevaplar, YALNIZCA İKİNCİLDİR (uzmanlık dışı). Ah, iyi uzmanlık günü yine de kazanıyor olduğundan, bu durumda genelliğin daha fazla (ve daha fazla ödüllendirici) olmadığını göstermek için cevabı düzenlememe izin verin ;-). –

+0

Beni yanlış yöne yanlış yorumladığınıza inanıyorum: IMHO orijinal funlooper * çok * genel. Belirli bir forma sahip dönüş değerine zaten bağlı olduğumuzdan, genel bir çağrılabilir geçişi geçmek yerine dosya benzeri ara yüzün (okuma yöntemi) bu kısmına bağlı kalmak mantıklıdır. Başarısız olması durumunda, kullanıcının en azından bağımlılığın farkında olması gerekir. –

1

Python'daki EOF işareti boş bir dizedir, böylece sahip olduğunuz şey, bir yineleyiciye sarmak için bir işlev yazmadan alacağınız en iyiye oldukça yakındır. Ben while gibi değiştirerek biraz daha pythonic şekilde yazılmış olabilir:

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

Bu, atamayı (çıkardığınız) önce len_name öğesine kopyalamayı gerektirir ve bu çoğaltmadan kaçınmak neredeyse her zaman istenir. –

5

Zaten bahsedilen yineleyici tabanlı çözüm bir for döngüsü çevirmeye tercih ederim.doğrudan yazılı Başka bir çözüm kolayca kendi jeneratörüne çekilir ve bir for döngüsü olarak kullanılan nasıl kıyasla Knuth'un "döngü ve bir buçuk"

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

görebilirsiniz olduğunu.

+0

Bu özel durumda, "iter()" çözümünü daha iyi sevdiğimi düşünüyorum, ama bunu düşünmemek için oldukça aptal hissediyorum. İyi bir senin için hak ettim. ;-) –

+0

Vay. Evet, bu iter() çözümü güzel. Bir "lambda:" ile birleştiğinde ve kapanmalara bağlı olarak bunu anlamak biraz daha zor, ama hiç de tatlı değil. –

0
ben okunabilirlik için fonksiyonu ve yineleyici yeniden Tendayi önerisi ile gitmek istiyorum

:

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

Nedeni yok, sadece bir araya getirdiğim bir şey. Snippet'i değiştirdim. –

İlgili konular