Jak pętli do EOF w Pythonie?
Muszę zapętlać, dopóki nie dotrę do końca obiektu podobnego do pliku, ale nie znajduję "oczywistego sposobu, aby to zrobić", co sprawia, że podejrzewam, że przeoczyłem coś, cóż, oczywistego. :-)
Mam stream (w tym przypadku jest to obiekt StringIO, ale ciekaw jestem również ogólnego przypadku), który przechowuje nieznaną liczbę rekordów w formacie " ", np.:
data = StringIO("\x07\x00\x00\x00foobar\x00\x04\x00\x00\x00baz\x00")
Teraz, jedynym jasnym sposobem, jaki mogę sobie wyobrazić, aby to odczytać, jest użycie (co myślę o) zainicjalizowanej pętli, co wydaje się trochę Nie-Pythoniczne:
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)
W języku podobnym do C, po prostu umieściłbym read(4)
w klauzuli testowej while
, ale oczywiście to nie zadziała w Pythonie. Jakieś pomysły na lepszy sposób, aby to osiągnąć?
6 answers
Możesz połączyć iterację poprzez iter () {[3] } z Wartownikiem:
for block in iter(lambda: file_obj.read(4), ""):
use(block)
Czy widziałeś jak iterację nad wierszami w pliku tekstowym?
for line in file_obj:
use(line)
Możesz zrobić to samo z własnym generatorem:
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)
Zobacz też:
Wolę już wspomniane rozwiązanie oparte na iteratorze, aby przekształcić to w pętlę for. Innym rozwiązaniem pisanym bezpośrednio jest "pętla-i-pół" Knutha
while 1:
len_name = data.read(4)
if not len_name:
break
names.append(data.read(len_name))
Możesz zobaczyć przez porównanie, jak łatwo jest go wciągnąć do własnego generatora i użyć jako pętli for.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 22:06:18
Widzę, zgodnie z przewidywaniami, że typowa i najpopularniejsza odpowiedź wykorzystuje bardzo wyspecjalizowane generatory do "odczytu 4 bajtów na raz". Czasami ogólność nie jest trudniejsza (i znacznie bardziej satysfakcjonująca; -), więc zaproponowałem następujące bardzo ogólne rozwiązanie: {]}
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
Teraz pożądany nagłówek pętli to po prostu: for len_name in funlooper(data.read, 4):
.
Edit : made much more general by the wearedone
idiom since a comment possigned my nieco mniej general previous version (hardcoding the exit test as if not data:
) posiadania "ukrytej zależności", ze wszystkich rzeczy!-)
Zwykły szwajcarski nóż wojskowy pętli, itertools
, też jest dobrze, oczywiście, jak zwykle:
import itertools as it
for len_name in it.takewhile(bool, it.imap(data.read, it.repeat(4))): ...
Lub, całkiem równoważnie:
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): ...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 23:05:30
Znacznik EOF w Pythonie jest pustym ciągiem znaków, więc to, co masz, jest prawie najlepsze, co możesz uzyskać bez pisania funkcji do zawinięcia tego w iteratorze. Można by napisać w nieco bardziej pythoniczny sposób zmieniając while
Jak:
while len_name:
len_name = struct.unpack("<I", len_name)[0]
names.append(data.read(len_name))
len_name = data.read(4)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 22:05:15
Wybrałbym funkcję Re sugestii Tendayi i iterator dla czytelności:
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-18 00:29:32