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 „ “ Format, zB:

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?

War es hilfreich?

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)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top