2013-02-16 3 views
1

json 개의 파일이 많은 .tar.bz2 개의 파일이 있습니다. 하나의 아카이브에는 수천 개가있을 수 있으며 jsons는 작습니다 (보통 10KB 미만, 보통 킬로바이트 미만). 결과적으로 압축 후 단일 아카이브는 100kB를 초과하지 않습니다.첫 번째 정규 파일 다음에 Tarfile이 중지됩니다.

the documentation에 따르면 다음 함수는 tar 파일의 모든 일반 파일에 대한 반복자를 반환하여 tarinfo 구조와 데이터를 반환해야합니다.

import tarfile 

def tariter(filename): 
    with tarfile.open(filename) as archive: 
     while True: 
      tarinfo = archive.next() 
      if tarinfo is None: 
       break 

      if tarinfo.isreg(): 
       handle = archive.extractfile(tarinfo.name) 
       data = handle.read() 
       handle.close() 

       yield tarinfo, data 

그러나 그 대신에 첫 번째 파일을 내용과 함께 반환하는 반복기를 반환 한 다음 중지합니다. 분명히, archive.next()은 아카이브에 많은 파일이 있더라도 두 번째 멤버를 읽은 후 없음을 반환합니다.

이 코드의 어딘가에 버그가 있습니까? next()가 (로컬뿐만 아니라 나를 위해 실패) 실패 이유

+0

'archive.next'가 None을 반환합니까? 이것은 또한'tarinfo.isreg()'에서 실패 할 수 있습니다. 거짓이면'break'가 호출 될 때까지 spinloop을 입력 할 수 있습니다. – slezica

+0

그래, 디버깅하기 위해'print' 문을 추가했습니다. 또한,'archive.next'는'None'을 리턴해야하지만, 아카이브의 끝에 도달 할 때만 ...'isreg()'는 디렉토리를 필터링하는 역할 만합니다. 내용은 내가 아는 한별로 중요하지 않아야한다. – liori

+0

방금이 코드를 로컬에서 시도 했으므로 동일한 결과가 나타납니다. 이것은 계약 문제이며 호기심을 불러 일으켰습니다. 내가 찾을 수있는 것을 보겠습니다. – slezica

답변

1

는 모르겠지만,이 작품 (그리고 청소기 모양) :

이익을 위하여 다음과 같은 원래 OP 코드를 변경 단지에 대한
import tarfile 

def tariter(filename): 
    with tarfile.open(filename) as archive: 
     for tarinfo in archive: 
      if tarinfo.isreg(): 
       handle = archive.extractfile(tarinfo.name) 
       data = handle.read() 
       handle.close() 

       yield tarinfo, data 
+0

설명서에는 TarFile 개체가 반복자 프로토콜을 지원한다고 나와 있지 않습니다 ... – liori

+0

문서 페이지에는이 사용법의 예가 나와 있습니다. – slezica

0

@upside 코드가 더 의미가 있지만 작동합니다.

import tarfile 
def tariter(filename): 
    with tarfile.open(filename) as archive: 
     it = archive.__iter__() # CHANGE 
     while True: 
      tarinfo = it.next() # CHANGE 
      if tarinfo is None: 
       break 

      if tarinfo.isreg(): 
       handle = archive.extractfile(tarinfo.name) 
       data = handle.read() 
       handle.close() 

       yield tarinfo, data 
+0

기본값은 자동 감지를 수행하는 것으로, 첫 번째 구성원을 읽을 수 있기 때문에 작동합니다. – liori

+0

오케이 - 나에게 나에게 비슷한 이슈가 있었는데 그 전에 읽은 것이 하나의 항목이었습니다. 아마도 카고 컬트 프로그래밍의 경우 였을 것입니다. – sotapme

2

해결 방법은 tarinfo 대신 직접 이름으로 extractfile을 사용하는 것입니다. 이 작품 :

이런 일이에 관해서는
def tariter(filename): 
    with tarfile.open(filename) as archive: 
     while True: 
      tarinfo = archive.next() 
      if tarinfo is None: 
       break 

      if tarinfo.isreg(): 
       handle = archive.extractfile(tarinfo) # LINE CHANGED 
       data = handle.read() 
       handle.close() 

       yield tarinfo, data 

: TarFile.next()None을 반환하는 대신 StopIteration 인상 때문에 하지 반복자 프로토콜을 구현한다.

이터레이터 프로토콜에는 반복자를 반환하는 컨테이너 요소의 "외부"부분과 반복자 자체 인 "내부"부분의 두 부분이 있습니다.

컨테이너는 을 구현해야하며, 이는 반복자 인 개체를 반환합니다. TarFile.__iter__()은 새 TarIter 개체를 반환합니다.

이터레이터 자체 (TarIter)는 __iter__() (항상 self을 반환) 및 next()을 구현합니다. 또한 원래 컨테이너의 항목에 대한 자체 인덱스가 있어야합니다. 이렇게하면 별도의 반복이 서로 어지럽히 지 않고 동일한 컨테이너에서 여러 개의 다른 반복자를 생성 할 수 있습니다. 다른 사람이 그들이 엉망 반복을 것이다 TarFile에서 제공하는 의사 반복 프로토콜을 사용 그래서 만약

TarFile.next() 그러나, 하지이 반복에 대해 별도의 인덱스를 사용한다.

이것은 여기에서 발생하는 것 같습니다. TarFile.extractfile(filename)은 현재 TarFile에있는 일치하는 파일이 TarFile.__iter__() 대신 사용 된 TarFile.next()을 사용하여 찾습니다. 이렇게하면 "다음 항목"색인이 손상되어 이 으로 돌아오고 첫 번째 extractfile() 호출 이후에 반환됩니다.당신이 extractfile(tarinfo)를 사용하는 경우

그러나 tarinfo 개체가 일치하는 파일 이름을 찾고 archive 객체를 통해 추구하지 않고 문자열 내용을 추출 할 수 TarFile하기에 충분한 메타 데이터를 가지고있다. 따라서 archive.extractfile(tarinfo)archive.extractfile(tarinfo.name)보다 빠릅니다.

일반적으로 컬렉션 개체 (예 : TarFile)는 이 아니고은 반복되지만 새 개체를 반복하여 생성해야합니다. TarFile.next()의 단순한 존재는 나쁜 디자인의 냄새를 풍깁니다. 아마도 좋은 이유가 있지만, 을 사용하지 않아도됩니다!

대신이 작업을 수행 :

def tariter(filename): 
    with tarfile.open(filename) as archive: 
     # use TarIter object for iteration over archive 
     for tarinfo in archive: 
      if tarinfo.isreg(): 
       handle = archive.extractfile(tarinfo) 
       data = handle.read() 
       handle.close() 
       yield tarinfo, data 

이 명확하고, 내가 너무 조금 더 빨리 내기 것이다.

+0

그리고 적어도 문서화되어 있습니다. 고맙습니다. – liori

+0

@liori, 왜 이런 일이 일어나고 있는지, 그리고 대체 코드를 사용해야하는지에 대한 자세한 설명을 추가했습니다. –

+0

2.7'tarfile' 문서가 "TarFile"이 반복자 프로토콜을 지원한다고 명시 적으로 말하지 않았 음에 틀림 없습니다. 그러나'TarFile'에는'__iter__' 속성이 있고 [두 번째 코드 예제] (http://docs.python.org/2.7/library/tarfile.html#examples)에는'tarinfo for members :'에 대한 설명이 있습니다. 용도. –

관련 문제