2009-11-17 14 views
9

私はファイルのようなオブジェクトの終わりに達するまでループする必要がありますが、私は "明白なやり方"を見つけていません。 :-)PythonでEOFまでループする方法は?

「< length> < data>」という形式の未知数のレコードを格納するストリームがあります(この場合、StringIOオブジェクトですが、これも一般的なケースに興味があります)。例えば:今すぐ

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

、私はこれが(私はのように考える何を)少し非Python的と思われる初期化されたループ、使用している読むことを想像することができる唯一の明確な方法:

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のような言語では、私は012にread(4)を貼り付けていますのテスト節ですが、もちろんPythonではうまくいきません。これを達成するためのよりよい方法に関する考えはありますか?

+0

非常に多くの回答を!おそらく、私は昼食に行く前に投稿してはいけません。 :-) –

答えて

24

あなたはセンチネルとiter()を通じて反復組み合わせることができます。私は読みやすくするための機能とイテレータ再Tendayiの提案で行くと思います

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

確かに最高のアンサー。あなたは私にこの1つを持っています、私はこれをとても忘れています。 –

+0

私はこれも最高のものが好きだと思います。あまりコードがないので、やっていることは非常にはっきりしています。助けてくれてありがとう! –

+1

ファイルを書いていた場合は、それを反復する前にkindonとrewind、つまりfile_obj.seek(0)をリマインダーしてください。 – mikemaccana

10

テキストファイルの行を反復処理する方法を確認しましたか?

for line in file_obj: 
    use(line) 

独自の発電機と同じことを行うことができます

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) 

も参照してください:

+0

ジェネレータのwhileループとしてループを構造化することもできます。最も読みやすいものを使用してください。 –

3

予測されるように、私は、参照、一般的なこと最も一般的な答えは、非常に特殊化されたジェネレータを使用して、 "時間"。時々、一般には、私の代わりに、以下の非常に一般的な解決策を提案してきた、どんな難しい(とはるか;-)やりがいありませんので、:

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 

は、今すぐあなたの所望のループヘッダだけです:for len_name in funlooper(data.read, 4):

編集は:コメントは、すべての物事の「隠された依存関係」を有するの(if not data:として終了テストをハードコーディング)私のわずかに少ない一般的な前のバージョンを非難したので、wearedoneイディオムではるかに一般的な製 - )

itertoolsをループの通常のスイスアーミーナイフは、いつものように、当然のことながら、細かすぎる:かなり同等

import itertools as it 

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

または、:

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

funlooperには、終わりを示すために真でない結果を返す関数が必要なため、隠れた依存関係があります。 –

+0

@ R.Pate、もちろん、funlooperに 'wearedone'述語の引数を' operator.not_'にデフォルトで追加し、 'if'を' wearedone(data):break'に変更することはもちろん可能です。私が確信したときに、この簡単なコードで答えをさらに一般化する価値があると思ってください(そして、それを修正します;-)他の答えは、特別な(余計なことはない)特別な方法です。ああ、過度の専門化が日にちに勝っているので、私は一般性がこのケースではそれほど難しくない(とはるかに報酬が高い)ことを示す答えを編集させてください。 –

+0

私はあなたが間違った方向に私を誤解したと思う:IMHO元のfunlooperも*一般*です。私たちはすでに特定のフォームを持つ戻り値に依存しているので、ここでは、汎用コール可能を渡すのではなく、ファイルのようなインターフェイス(readメソッド)のこの部分に依存するのが妥当です。それに失敗した場合、ユーザーは少なくとも依存関係を認識していなければなりません。 –

1

PythonのEOFマーカーは空の文字列なので、これをイテレータでラップする関数を記述しなくても、得られる最良のものにかなり近いです。私はwhileなどを変更することで、もう少し神託のように書くことができます。

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

これはループの前にlen_nameへの代入を複製する必要があります。これはほとんどの場合、この複製を避けることが望まれます。 –

5

私はすでに述べた反復子ベースのソリューションは、forループの中にこれを有効にすることを好みます。直接書き込ま別の解決策は、あなたがそれを簡単に、独自のジェネレータに掲揚し、forループとして使われていますどのように比較することによって見ることができます

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

Knuthの「ループ半」です。

+0

この特定のケースでは、私はiter()の方が良いと思いますが、これを考えなかったのはかなり愚かだと思います。あなたのためにも値する+1。 ;-) –

+0

うわー。ええ、iter()の解決策は素晴らしいです。 "lambda:"と組み合わせて、閉鎖に応じて、理解するのが少し難しくなりますが、甘いものはありません。 –

0

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

理由はありません。私はスニペットを修正しました。 –

関連する問題