Wie man Schleife, bis EOF in Python?
Frage
ich Schleife brauchen, bis ich das Ende einer Datei ähnlichen Hit Objekt, aber ich finde keine „offensichtliche Art und Weise, es zu tun“, das macht mich vermuten, dass ich etwas mit Blick auf, na ja, offensichtlich. : -)
Ich habe einen Strom (in diesem Fall ist es ein StringIO Objekt, aber ich bin neugierig auf den allgemeinen Fall auch) die Geschäfte eine unbekannte Anzahl von Datensätzen in „
data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00")
Nun, die einzige klare Art und Weise kann ich mir vorstellen, dies zu lesen ist mit (was ich denke, der als) eine initialisierte Schleife, die ein wenig un-Pythonic scheint:
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)
In einer C-ähnlichen Sprache, würde ich nur Stick die read(4)
in der Test Klausel while
, aber das ist natürlich nicht funktionieren wird für Python. Irgendwelche Gedanken auf einem besseren Weg, dies zu erreichen?
Lösung
Sie können kombinieren Iteration durch iter () mit einem Sentinel:
for block in iter(lambda: file_obj.read(4), ""):
use(block)
Andere Tipps
Haben Sie gesehen, wie Linien iterieren in einer Textdatei?
for line in file_obj:
use(line)
Sie können mit Ihrem eigenen Generator das gleiche tun:
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)
Siehe auch:
ziehe ich die bereits erwähnten Iterator-basierte Lösung dieses in eine for-Schleife zu drehen. Eine weitere Lösung direkt geschrieben ist Knuth "Loop-and-a-half"
while 1:
len_name = data.read(4)
if not len_name:
break
names.append(data.read(len_name))
Sie können im Vergleich zu sehen, wie das leicht in einen eigenen Generator hochgezogen ist und als for-Schleife verwendet wird.
Ich sehe, wie vorhergesagt, dass die typische und beliebtestene Antwort verwendet sehr spezialisiert Generatoren zu „las 4 Bytes zu einer Zeit“. Manchmal Allgemeinheit nicht schwerer ist (und viel lohnender ;-), so, ich habe stattdessen die folgende sehr allgemeine Lösung vorgeschlagen:
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
Ihr gewünschter Schleifenkopf ist einfach:. for len_name in funlooper(data.read, 4):
Bearbeiten : viel allgemeiner durch das wearedone
Idiom, seit einem Kommentar meint etwas weniger allgemeine Vorversion beschuldigt (hartzucodieren den Ausgang Test als if not data:
) vor, „eine versteckte Abhängigkeit“, alle Dinge! -)
Die übliche Schweizer Taschenmesser von looping, itertools
, ist auch in Ordnung, von natürlich wie immer:
import itertools as it
for len_name in it.takewhile(bool, it.imap(data.read, it.repeat(4))): ...
oder, ganz äquivalent:
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): ...
Die EOF-Markierung in Python ist eine leere Zeichenfolge so, was Sie haben, ist ziemlich nahe am besten Sie ohne das Schreiben eine Funktion erhalten werden diese in einem Iterator einpacken. Ich kann wie durch Änderung der while
in etwas mehr pythonic Weise geschrieben werden:
while len_name:
len_name = struct.unpack("<I", len_name)[0]
names.append(data.read(len_name))
len_name = data.read(4)
würde ich mit Tendayi Vorschlag re Funktion gehen und Iterator zur besseren Lesbarkeit:
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)