2008-11-05 1 views
4

파이썬의 gzip 모듈을 사용하여 데이터 압축에 관심이 있습니다. gzip을 사용하지 않는 일부 프로세스가 출력의 변경 사항을 찾거나, 또는 출력을 변경하려고하는 경우, 압축 된 출력이 결정적이되기를 원합니다. 출력은 암호로 서명 될 것입니다.파이썬에서 gzip 타임 스탬프를 설정하십시오.

불행히도 출력은 매번 다릅니다. 내가 말할 수있는 한, 파이썬 모듈이 항상 현재 시간으로 채우는 gzip 헤더의 타임 스탬프 필드가 유일한 이유입니다. 나는 당신이 실제로 타임 스탬프가없는 gzip 스트림을 가질 수 있다고 생각하지 않는다. 그것은 너무 나쁘다.

어쨌든 Python의 gzip 모듈 호출자가 기본 데이터의 올바른 수정 시간을 제공하는 방법이없는 것처럼 보입니다. (실제 gzip 프로그램은 가능한 경우 입력 파일의 타임 스탬프를 사용하는 것 같습니다.) 기본적으로 타임 스탬프를 염려하는 유일한 이유는 파일에 쓸 때 gunzip 명령이기 때문입니다. 나는 결정 론적 결과를 원하기 때문입니다. 그렇게 많이 물어볼 수 있습니까?

이 문제가 발생한 사람이 있습니까?

gzip 파이썬에서 임의의 타임 스탬프가있는 일부 데이터는 무엇이 가장 무시 무시한 방법일까요?

답변

3

파이썬 2.7 이상에서는 gzip 헤더에서 사용할 시간을 지정할 수 있습니다. N.B. filename은 또한 헤더에 포함되며 수동으로 지정할 수도 있습니다. 도미닉에서 답 유사

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() 
+0

그래, 나는 패치가 잠시 후 받아 들여 졌다고 생각한다! 시간 파리 .... – zaphod

+0

나를 위해 작동하지 않습니다 - 헤더 여전히 시간이 걸립니다 –

0

lib/gzip.py에서 실제로 타임 스탬프를 포함하는 부분을 포함하여 헤더를 작성하는 메소드를 찾습니다. 당신이 볼 수 있듯이

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을 사용하여 파이썬 2.5에서,이 라인 (143)에 시작한다. 온라인 모듈 문서에 따르면 time.time은 시간을 UTC로 그 기원 이후 초 단위로 나타낸 부동 소수점 숫자로 반환합니다. 따라서 이것을 부동 소수점 상수로 변경하면 항상 동일한 헤더를 쓸 수 있습니다. 라이브러리를 더 해킹하여 time.time()을 지정하지 않은 상태에서 사용하는 선택적 time 매개 변수를 허용하도록 지정하지 않으면 더 좋은 방법을 찾을 수 없습니다. 지정되지 않은 경우이 경우 확실합니다. 당신이 패치를 제출하면 그들은 그것을 좋아할 것입니다!

8

그래, 당신은 예쁜 옵션이 없습니다. 시간은 _write_gzip_header이 선으로 기록됩니다

  1. 은을 GzipFile에서 클래스를 파생 : 그들이 당신에게 시간을 오버라이드 (override) 할 수있는 방법을 제공하지 않기 때문에

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

    , 당신은 이런 것들 중 하나를 수행 할 수 있습니다 _write_gzip_header 함수를 파생 된 클래스에 복사하지만이 한 줄에 다른 값을 복사하십시오.

  2. gzip 모듈을 가져온 후 해당 시간 구성원에 새 코드를 할당하십시오. 근본적으로 gzip 코드에서 이름 시간에 대한 새로운 정의를 제공 할 것이므로 time.time()의 의미를 변경할 수 있습니다.
  3. 전체 gzip 모듈을 복사하고 이름을 my_stable_gzip으로 지정하고 필요한 줄을 변경하십시오.
  4. CStringIO 객체를 fileobj로 전달하고 gzip이 완료된 후 bytestream을 수정합니다.
  5. 작성된 바이트를 추적하는 가짜 파일 오브젝트를 작성하고, 사용자가 직접 작성한 시간 소인의 바이트를 제외한 모든 내용을 실제 파일로 전달합니다.

    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 모듈의 내부에 의존하지 않는 점에서 (안된) 깨끗한 수있다 :

    여기

2 (안된) 옵션 번호의 예

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) 
0

꽤 아니지만,이 같은 뭔가 임시로 time.time을 monkeypatch 수 :

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 

예쁜 것은 아니지만 아마도 효과가있을 것입니다.

+0

이 방법 내 주요 이의 나 '이다 라이브러리를 작성하고, 내 라이브러리의 호출자가 다른 스레드에서 gzip을 사용하려고 시도했을 수 있습니다.이 경우 변경 사항은 다른 스레드에 잠재적으로 영향을 미칩니다.다른 스레드가 같은 트릭을 사용하려고하면 이것은 특히 위험합니다! – zaphod

2

patch을 제출하여 타임 스탬프 계산을 내 렸습니다. 거의 받아 들여질 것입니다.

+0

패치가 우분투에 나타날 것이라는 것을 상상할 수는 없습니다. 아직도, 나는 이것이 훌륭한 대답이라고 생각합니다! – zaphod

1

저는 코벤트리 씨의 조언을 받아 들였습니다. submitted a patch. 그러나 Python 릴리스 일정의 현재 상태를 보면 3.0이 곧 출시 될 예정이며 언제든지 곧 릴리스 될 것으로 예상하지는 않습니다. 그래도 우리는 무슨 일이 일어날 지 알게 될 것입니다!

그동안 나는 timestamp 필드를 올바르게 설정하는 작은 사용자 정의 필터를 통해 gzip 스트림을 파이핑하는 Mr. Batchelder의 옵션 5를 좋아합니다. 가장 깨끗한 접근법처럼 들립니다. 그가 보여 주듯이, 그의 예제는 gzip 모듈 구현이 write()에 대한 정확히 하나의 4 바이트 호출을 사용하여 타임 스탬프를 작성하기로 선택할 것이라는 (현재 유효한) 가정에 대한 단순함에 달려 있지만 실제로 필요한 코드는 아주 작습니다. 그러나 필요한 경우 완전히 일반 버전을 찾는 것이 매우 어려울 것이라고 생각하지 않습니다.

원숭이 패치 방식 (일명 옵션 2)은 단순함으로 인해 유혹에 빠졌지 만 독립형 프로그램이 아니라 gzip이라는 라이브러리를 작성하고 있기 때문에 잠시 멈출 수 있습니다. 누군가가 시도해 볼 수있는 것처럼 보입니다. 모듈이 gzip 모듈의 전역 상태로 변경하기 전에 다른 스레드에서 gzip을 호출해야합니다. 다른 스레드가 비슷한 원숭이 패치 스턴트를 가져 오려고했다면 이것은 특히 불행한 일입니다! 나는이 잠재적 인 문제가 실제로 일어날 가능성이 높지 않다고 인정하지만 그러한 혼란을 진단하는 것이 얼마나 고통 스러울 지 상상해보십시오!

왠지 너무 미래 지향적 어떻게 든 gzip 모듈과 원숭이 패치 그 개인 사본을 가져 까다 롭고 복잡하고 아마도없는 일을하려고 상상할 수 있지만, 그 시점에서 필터가 간단하고 보인다 곧장.

0

위의하지만 기존 파일 :

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