سؤال

أنا مهتم بضغط البيانات باستخدام لغة بايثون gzip وحدة.يحدث أنني أريد أن يكون الإخراج المضغوط حتميًا، لأن هذا غالبًا ما يكون خاصية ملائمة حقًا للأشياء بشكل عام - إذا كانت بعض العمليات غير المدركة لـ gzip ستبحث عن تغييرات في الإخراج، على سبيل المثال، أو إذا سيتم توقيع الإخراج بشكل مشفر.

لسوء الحظ، الإخراج يختلف في كل مرة.بقدر ما أستطيع أن أقول، السبب الوحيد لذلك هو حقل الطابع الزمني في رأس gzip، والذي تملأه وحدة Python دائمًا بالوقت الحالي.لا أعتقد أنه يُسمح لك فعليًا بالحصول على دفق gzip بدون طابع زمني، وهو أمر سيء للغاية.

على أية حال، لا يبدو أن هناك طريقة للمتصل ببايثون gzip وحدة لتوفير وقت التعديل الصحيح للبيانات الأساسية.(الفعلية gzip يبدو أن البرنامج يستخدم الطابع الزمني لملف الإدخال عندما يكون ذلك ممكنًا.) أتصور أن السبب في ذلك هو أن الشيء الوحيد الذي يهتم بالطابع الزمني هو gunzip الأمر عند الكتابة إلى ملف - والآن أنا، لأنني أريد مخرجات حتمية.هل هذا كثير على لاطلبه؟

هل واجه أحدا آخر هذه المشكلة؟

ما هي الطريقة الأقل فظاعة ل gzip بعض البيانات ذات الطابع الزمني التعسفي من بايثون؟

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

المحلول

ومن بيثون 2.7 فصاعدا يمكنك تحديد الوقت المناسب لاستخدامها في رأس غزيب. حاشية كما يتم تضمين اسم الملف في الرأس ويمكن أيضا تحديد يدويا.

import gzip

content = b"Some content"
f = open("/tmp/f.gz", "wb")
gz = gzip.GzipFile(fileobj=f,mode="wb",filename="",mtime=0)
gz.write(content)
gz.close()
f.close()

نصائح أخرى

نعم، ليس لديك أي خيارات جميلة.يتم كتابة الوقت بهذا السطر في _write_gzip_header:

write32u(self.fileobj, long(time.time()))

وبما أنهم لا يوفرون لك طريقة لتجاوز الوقت، فيمكنك القيام بأحد هذه الأشياء:

  1. اشتقاق فئة من GzipFile، وانسخ ملف _write_gzip_header تعمل في فئتك المشتقة، ولكن بقيمة مختلفة في هذا السطر الواحد.
  2. بعد استيراد وحدة gzip، قم بتعيين رمز جديد لعضو الوقت الخاص بها.ستوفر بشكل أساسي تعريفًا جديدًا لاسم الوقت في الرمز البريدي gzip، حتى تتمكن من تغيير معنى time.time().
  3. انسخ وحدة gzip بأكملها، وقم بتسميتها my_stable_gzip، وقم بتغيير السطر الذي تحتاجه.
  4. قم بتمرير كائن CStringIO كـ fileobj، وقم بتعديل تدفق البايت بعد الانتهاء من gzip.
  5. اكتب كائن ملف مزيفًا يتتبع البايتات المكتوبة، ويمرر كل شيء إلى ملف حقيقي، باستثناء بايتات الطابع الزمني، التي تكتبها بنفسك.

إليك مثال على الخيار رقم 2 (لم يتم اختباره):

class FakeTime:
    def time(self):
        return 1225856967.109

import gzip
gzip.time = FakeTime()

# Now call gzip, it will think time doesn't change!

قد يكون الخيار رقم 5 هو الأنظف من حيث عدم الاعتماد على الأجزاء الداخلية لوحدة gzip (لم يتم اختبارها):

class GzipTimeFixingFile:
    def __init__(self, realfile):
        self.realfile = realfile
        self.pos = 0

    def write(self, bytes):
        if self.pos == 4 and len(bytes) == 4:
            self.realfile.write("XYZY")  # Fake time goes here.
        else:
            self.realfile.write(bytes)
        self.pos += len(bytes)

التصحيح التي يتم الحسبان عند حساب الطابع الزمني بها. يكاد يكون من المؤكد أن تقبل ذلك.

ولقد اتخذت المشورة السيد كوفنتري و قدم التصحيح . ومع ذلك، وبالنظر إلى الوضع الحالي للالافراج عن الجدول الزمني بيثون، مع 3.0 قاب قوسين أو أدنى، وأنا لا أتوقع أن تظهر في بيان في وقت قريب. ومع ذلك، فإننا سوف نرى ما سيحدث!

في غضون ذلك، أنا أحب الخيار السيد باتشلدر في 5 من الأنابيب تيار غزيب من خلال تصفية مخصص الصغير الذي يحدد مجال زمني بشكل صحيح. هذا يبدو وكأنه نهج أنظف. كما انه يدل، رمز المطلوب هو في الواقع صغيرة جدا، على الرغم من مثاله لا يتوقف لبعض بساطته على افتراض (ساري المفعول حاليا) أن تنفيذ وحدة gzip سوف تختار لإرسال الطابع الزمني باستخدام احد بالضبط دعوة أربعة بايت لwrite(). ومع ذلك، لا أعتقد أنه سيكون من الصعب جدا التوصل الى صيغة عامة تماما إذا لزم الأمر.

وونهج الترقيع قرد (ويعرف أيضا باسم الخيار 2) هو مغريا جدا لبساطته ولكن يعطيني قفة لأنني أكتب مكتبة يدعو gzip، وليس مجرد برنامج مستقل، ويبدو لي أن شخصا ما قد يحاول استدعاء gzip من موضوع آخر قبل وحدة بلادي مستعدة لعكس التغيير لدولة العالمية وحدة gzip ل. فهذا سيكون أمرا سيئا خاصة إذا كان موضوع آخرين كانوا يحاولون سحب مماثلة-الترقيع قرد حيلة! أعترف لا يبدو هذه المشكلة المحتملة من المرجح جدا أن يأتي في الممارسة، ولكن تخيل كيف مؤلمة سيكون لتشخيص مثل هذه الفوضى!

ويمكنني غامضة تخيل تحاول أن تفعل شيء صعب ومعقد وربما ليس كذلك في المستقبل واقية لاستيراد بطريقة أو بأخرى نسخة خاصة من وحدة gzip وقرد التصحيح <م> أن ، ولكن هذه النقطة مرشح يبدو أكثر بساطة ومباشرة.

في ليب / gzip.py، نجد الطريقة التي يبني الرأس، بما في ذلك الجزء الذي لا يحتوي في الواقع الطابع الزمني. في بيثون 2.5، وهذا يبدأ على خط 143:

def _write_gzip_header(self):
    self.fileobj.write('\037\213')             # magic header
    self.fileobj.write('\010')                 # compression method
    fname = self.filename[:-3]
    flags = 0
    if fname:
        flags = FNAME
    self.fileobj.write(chr(flags))
    write32u(self.fileobj, long(time.time())) # The current time!
    self.fileobj.write('\002')
    self.fileobj.write('\377')
    if fname:
        self.fileobj.write(fname + '\000')

وكما ترون، فإنه يستخدم time.time () لجلب الوقت الحالي. وفقا لمستندات وحدة على الانترنت، time.time سوف "العودة مرة كرقم النقطة العائمة التي أعرب عنها في الثواني منذ الحقبة، في UTC." لذلك، إذا قمت بتغيير هذا ثابت الفاصلة العائمة من اختيارك، هل يمكن أن يكون دائما نفس رؤوس مكتوبة بها. لا استطيع ان ارى طريقة أفضل للقيام بذلك إلا إذا كنت تريد الإختراق مكتبة بعض أكثر لقبول وقت المعلمة اختياري التي تستخدمها في حين تعثر على time.time () عندما لا تحديده، وفي هذه الحالة، وأنا متأكد أنها أحب ذلك إذا قدم التصحيح!

وانها ليست جميلة، ولكن هل يمكن monkeypatch time.time مؤقتا مع شيء من هذا القبيل:

import time

def fake_time():
  return 100000000.0

def do_gzip(content):
    orig_time = time.time
    time.time = fake_time
    # result = do gzip stuff here
    time.time = orig_time
    return result

وانها ليست جميلة، ولكن على الأرجح أن ينجح.

وعلى غرار الجواب من فوق دومينيك، ولكن لعلى ملف موجود:

with open('test_zip1', 'rb') as f_in, open('test_zip1.gz', 'wb') as f_out:
    with gzip.GzipFile(fileobj=f_out, mode='wb', filename="", mtime=0) as gz_out:
         shutil.copyfileobj(f_in, gz_out)

ومبالغ اختبار MD5:

md5sum test_zip*
7e544bc6827232f67ff5508c8d6c30b3  test_zip1
75decc5768bdc3c98d6e598dea85e39b  test_zip1.gz
7e544bc6827232f67ff5508c8d6c30b3  test_zip2
75decc5768bdc3c98d6e598dea85e39b  test_zip2.gz
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top