2013-02-16 55 views
1

.tar.bz2文件,有很多小json文件。一個存檔可能有大約數千個,並且jsons很小(低於10kB,通常也低於千字節)。因此,壓縮後的單個壓縮文件不會超過100kB。tar文件停止後第一個常規文件

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()在讀完第二個成員之後返回無,即使歸檔文件很多。

難道我有一個錯誤的地方在這個代碼?

+0

你確定'archive.next'返回None嗎?這也可能在'tarinfo.isreg()'上失敗,如果這是錯誤的,它可能會輸入一個spinloop,直到調用'break'。 – slezica 2013-02-16 18:17:20

+0

是的,我添加了一個'print'語句來調試它。另外,它被記錄爲'archive.next'應該返回'None',但是隻有當它到達歸檔的末尾時......也是,'isreg()'只是爲了過濾目錄,因爲讀取它們沒有意義內容,據我所知這應該無關緊要。 – liori 2013-02-16 18:20:19

+0

我剛剛在本地嘗試了這段代碼,並得到了與您相同的結果。這是一個合同問題,你引發了我的好奇心:)我會看看我能找到什麼 – slezica 2013-02-16 18:22:45

回答

1

我不知道爲什麼next()失敗(失敗,我在當地也是),但這個工程(而且看上去清潔劑):

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 2013-02-16 18:36:48

+0

文檔頁面給出了這種用法的例子,但是 – slezica 2013-02-16 18:37:26

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

The默認是執行自動檢測,並且它可以工作,因爲它可以讀取第一個成員。 – liori 2013-02-16 18:44:24

+0

好吧 - 我不好意思,我以前曾經有過類似的問題,在那裏讀了一個單一的條目。也許這是我的貨物邪教編程案例。 – sotapme 2013-02-16 18:48:25

2

解決方法是直接使用extractfile與tarinfo而不是名稱。這工作:

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

有兩個部分,以迭代器協議:該返回一個迭代容器元件上的「外」部和「內」部是迭代器本身。

容器必須實現__iter__(),它返回一個新的對象,它是迭代器。 TarFile.__iter__()返回一個新的TarIter對象。

迭代器本身(TarIter)實現了__iter__()(總是返回self)和next()。它還必須具有自己的獨立索引來指向原始容器中的項目。這可以讓你在同一個容器上生成幾個不同的迭代器,而不會讓單獨的迭代互相混淆。

TarFile.next(),但是,確實使用單獨的索引的迭代,所以如果其他人使用由TarFile他們會弄亂迭代所提供的僞迭代協議。

這似乎是這裏發生了什麼。 TarFile.extractfile(filename)查找使用的TarFile.next()代替TarFile.__iter__()這是你用什麼在當前TarFile匹配的文件。這會破壞「下一項」索引,導致archive.next()在第一次調用extractfile()後返回None

但是,如果你使用extractfile(tarinfo),該tarinfo對象中有足夠的元數據TarFile提取字符串內容,而不通過archive對象尋找匹配文件名查找。因此,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 2013-02-17 19:25:05

+0

@liori,我加了一個很長的解釋,說明爲什麼會發生這種情況,並且應該使用一些替代代碼。 – 2013-02-17 19:27:38

+0

你說得對,2.7'tarfile'文檔沒有明確說「'TarFile'支持迭代器協議」。然而,'TarFile'具有'__iter__'屬性,第二個代碼示例(http://docs.python.org/2.7/library/tarfile.html#examples)在成員中說:「對於tarinfo:',展示它的使用。 – 2013-02-17 19:40:59

相關問題