سؤال

أحتاج إلى حلقة حتى أضغط على نهاية كائن يشبه الملف ، لكنني لا أجد "طريقة واضحة للقيام بذلك" ، مما يجعلني أشك في أنني أطل على شيء ما ، واضحًا ، واضحًا. :-)

لدي دفق (في هذه الحالة ، إنه كائن Stringio ، لكنني فضولي بشأن الحالة العامة أيضًا) الذي يخزن عددًا غير معروف من السجلات ""u003Clength>u003Cdata> "التنسيق ، على سبيل المثال:

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

الآن ، الطريقة الواضحة الوحيدة التي يمكنني أن أتخيلها لقراءة هذا هي استخدام (ما أفكر فيه) حلقة تهيئة ، والتي تبدو غير متوفرة بعض الشيء:

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 ، سألتزم فقط read(4) في ال whileبند الاختبار ، ولكن بالطبع لن يعمل لبيثون. أي أفكار حول طريقة أفضل لإنجاز هذا؟

هل كانت مفيدة؟

المحلول

يمكنك الجمع بين التكرار من خلال iter () مع الحارس:

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

نصائح أخرى

هل رأيت كيفية التكرار على الخطوط في ملف نصي؟

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)

أنظر أيضا:

أنا أفضل الحل القائم على التكرار المذكور بالفعل لتحويل هذا إلى حلقة. حل آخر مكتوب مباشرة هو "حلقة ونصف" Knuth

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

يمكنك أن ترى بالمقارنة كيف يتم رفع هذا بسهولة إلى مولدها الخاص واستخدامه كحمة.

أرى ، كما كان متوقعًا ، أن الإجابة النموذجية والأكثر شعبية تستخدم مولدات متخصصة جدًا "لقراءة 4 بايت في وقت واحد". في بعض الأحيان ، لا تكون الأعمدة أكثر صعوبة (وأكثر مجزية ؛-) ، لذلك ، اقترحت بدلاً من ذلك الحل العام التالي:

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):.

تعديل: جعل أكثر بكثير من قبل wearedone المصطلح نظرًا لأن التعليق اتهم الإصدار السابق الأقل عموماً ( if not data:) من "تبعية خفية" ، من كل الأشياء!-)

سكين الجيش السويسري المعتاد من الحلقات ، 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): ...

علامة EOF في Python عبارة عن سلسلة فارغة ، لذا فإن ما لديك قريب جدًا من أفضل ما ستحصل عليه دون كتابة وظيفة لالتفاف ذلك في مؤلف. يمكن أن أكون مكتوبًا بطريقة أكثر بيثونيًا عن طريق تغيير while مثل:

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

سأذهب مع اقتراح Tendayi إعادة وظيفة وتكرار لقدرة على القراءة:

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top