2011-02-08 1 views
12

계측에서 나온 데이터를 gzip 스트림으로 사용할 수있는 스크립트를 작성 중입니다. 약 90 %의 경우에 gzip 모듈이 완벽하게 작동하지만 일부 스트림에서는 IOError: Not a gzipped file을 생성합니다. gzip 헤더가 제거되고 수축 된 스트림이 zlib으로 직접 입력되면 나는 대신 Error -3 while decompressing data: incorrect header check을 얻습니다. 벽에 머리를 대고 약 반나절 만 지나면, 문제가있는 스트림에 마지막에 추가 된 여분의 바이트 (gzip 데이터의 일부가 아닌)가 겉보기에 무작위로 포함되어 있음을 발견했습니다.추가 데이터가 포함 된 Gzip 파일을 사용하려면 어떻게해야합니까?

그것은 파이썬은 두 가지 이유에서이 파일을 사용하여 작업 할 수없는 이상한 나를 친다 : Gzip으로하고 7zip과 모두 문제없이이 "패딩"파일을 열 수 있습니다

  1. . (. Gzip으로 메시지 decompression OK, trailing garbage ignored가, 7zip과 조용히 성공 생산)
  2. 것은 모두 Gzip으로 파이썬 문서는이 일을해야 함을 표시하는 것 (강조 광산)

    Gzip's format.txt :

    그것은 가능해야한다 은 압축 된 데이터의 실제 크기에 관계없이 모든 압축 방법 으로 압축 된 데이터의 끝을 감지합니다. 특히 압축 해제를 감지하고 이동 를 첨부 된 추가 데이터를 유효한 압축 파일에 기록 지향 파일 시스템에, 또는 수 있어야 압축 데이터 만 의 배수 장치로부터 판독 될 때 특정 블록 크기.

    Python's gzip.GzipFile`

    : 당신이 압축 된 데이터
    후 많은 자료를 추가 할 수 있기 때문 fileobj, 을 닫지 않는 GzipFile 객체의 close() 메서드를 호출

    . 또한 StringIO 개체를 쓰기 위해 열어 fileobj으로 전달하고 StringIO 개체의 getvalue() 메서드를 사용하여 결과 메모리 버퍼를 검색 할 수 있습니다.

    Python's zlib.Decompress.unused_data

    :

    압축 된 데이터의 끝을지나 모든 바이트를 포함하는 문자열입니다. 즉, 압축 데이터가 포함 된 마지막 바이트가 사용 가능할 때까지이 값은 ""입니다. 전체 문자열이 압축 된 데이터를 포함하고있는 것으로 밝혀지면이 문자열은 빈 문자열 ""입니다.

    압축 된 데이터 문자열이 실제로 끝나는 곳을 확인하는 유일한 방법은 실제로 압축을 풀는 것입니다. 즉, 압축 된 데이터가 큰 파일의 일부로 포함되어있는 경우 데이터를 읽은 다음 비어 있지 않은 문자열을 뒤 따르고 메서드로 끝나는 부분을 찾을 수 있으며 unused_data 특성이 더 이상 존재하지 않을 때까지 빈 문자열여기

내가 해봤 네 가지 방법을 제공합니다. (이 예제는 Python 3.1이지만 2.5와 2.7을 테스트했고 같은 문제가있었습니다.)

# approach 1 - gzip.open 
with gzip.open(filename) as datafile: 
    data = datafile.read() 

# approach 2 - gzip.GzipFile 
with open(filename, "rb") as gzipfile: 
    with gzip.GzipFile(fileobj=gzipfile) as datafile: 
     data = datafile.read() 

# approach 3 - zlib.decompress 
with open(filename, "rb") as gzipfile: 
    data = zlib.decompress(gzipfile.read()[10:]) 

# approach 4 - zlib.decompressobj 
with open(filename, "rb") as gzipfile: 
    decompressor = zlib.decompressobj() 
    data = decompressor.decompress(gzipfile.read()[10:]) 

잘못된 것이 있습니까? gzip에 문제가 모듈에서 버그가 수 있도록 보인다 동안

UPDATE는

좋아, 내 zlib 문제가 자초. ;-)

gzip.py을 파고 들자 내가 잘못하고있는 것을 깨달았습니다. 기본적으로 zlib.decompress 외. 벌거 벗은 스트림이 아닌 zlib-wrapped 스트림을 기대하십시오. wbits에 음수 값을 전달하면 zlib에 zlib 헤더를 건너 뛰고 원시 스트림의 압축을 해제하도록 지정할 수 있습니다. 두 가지 모두 작동합니다.

# approach 5 - zlib.decompress with negative wbits 
with open(filename, "rb") as gzipfile: 
    data = zlib.decompress(gzipfile.read()[10:], -zlib.MAX_WBITS) 

# approach 6 - zlib.decompressobj with negative wbits 
with open(filename, "rb") as gzipfile: 
    decompressor = zlib.decompressobj(-zlib.MAX_WBITS) 
    data = decompressor.decompress(gzipfile.read()[10:]) 

답변

18

이것은 버그입니다. 파이썬의 gzip 모듈의 품질은 파이썬 표준 라이브러리에서 요구되는 품질보다 훨씬 떨어집니다.

여기서 문제는 gzip 모듈이 파일이 gzip 형식 파일의 스트림이라고 가정한다는 것입니다. 압축 된 데이터의 끝에서 새로운 gzip 헤더를 기대하면서 처음부터 시작합니다. 하나도 찾지 못하면 예외가 발생합니다. 이것은 잘못된 것입니다. 물론

, 그것은 는 예를 들어, 두 개의 GZIP 파일을 연결하는 유효한입니다 :

echo testing > test.txt 
gzip test.txt 
cat test.txt.gz test.txt.gz > test2.txt.gz 
zcat test2.txt.gz 
# testing 
# testing 

gzip으로 모듈의 오류가 더 GZIP 주위에 두 번째 헤더하지있을 경우에 예외를 제기하지해야한다는 것입니다을; 단순히 파일을 끝내야합니다. 은 헤더가 처음으로 없으면 예외를 발생시킵니다.

gzip 모듈을 직접 수정하지 않고도 깨끗한 해결 방법은 없습니다. 그렇게하고 싶다면 _read 메소드의 맨 아래를보십시오. 다른 플래그를 설정해야합니다 (예 : 인 경우 _read_gzip_header에게 IOError 대신 EOFError을 발생 시키라고 말하십시오.

이 모듈에는 다른 버그가 있습니다. 예를 들어, 불필요하게 탐색하여 네트워크 소켓과 같이 식별 할 수없는 스트림에서 오류를 발생시킵니다. 이 모듈에 대한 확신이 거의 없습니다. gzip을 탐색하지 않고도 작동해야한다는 것을 모르는 개발자는 Python 표준 라이브러리를 위해 구현할 수있는 자격이 부적절합니다.

+0

문제를 디버깅하는 동안 실제로 gzip'의 내부 '와 비트에 대한 청소 만 싶지만, 그것을 해결하기 위해 나에게 발생하지 않았다 거기서 문제가 발생하여 수정 된 모듈을 내 스크립트로 패키지화하십시오.지옥처럼 추악하지만, 여전히 최상의 선택 옵션이 될 수 있습니다. : -/ –

+0

@Ben : 최소한의 비용은 들지 않는다. 하나의 파일. 네이티브 모듈로 인해 더 귀찮습니다. –

+0

해결 방법 : 코드의 크기 또는 시간 제약 조건을 위반하지 않는다고 가정하면 오류를받은 후 한 번에 한 바이트 씩 읽을 수 있습니다. 'gzipped 파일이 아닙니다'매개 변수가있는 다른 IOError를 수신하면 목록의 각 바이트를 추가하여 데이터 끝에 도달했습니다. ''.join을 반환하고 –

4

나는 과거에도 비슷한 문제가있었습니다. 스트림으로 더 잘 작동하는 new module이라고 썼습니다. 당신은 그것을 밖으로 시도하고 그것이 당신을 위해 작동하는지 볼 수 있습니다.

+3

수정 사항을 표준 gzip 모듈에 병합하는 것으로 생각한 적이 있습니까? – Karmastan

+0

나는 그것을 고려했지만 그것이 그대로있는 프레임 워크의 다른 부분에 대한 의존성을 가지고있다. 그러나 쉽게 고칠 수 있어야한다. – Keith

-1

나는 위에서 언급 한 기술로 작동시키지 못했습니다. 그래서

import zipfile 
from io import BytesIO 
mock_file = BytesIO(data) #data is the compressed string 
z = zipfile.ZipFile(file = mock_file) 
neat_data = z.read(z.namelist()[0]) 

가 완벽하게 작동 패키지를 ZipFile를 사용하여 주위에 작업을했다