在Python中读取.tar.gz文件

38

我有一个25GB的文本文件。所以我将它压缩成tar.gz格式,大小变为450MB。现在我想从Python中读取该文件并处理文本数据。我参考了 这个问题。但是在我的情况下,代码无法工作。代码如下:

import tarfile
import numpy as np 

tar = tarfile.open("filename.tar.gz", "r:gz")
for member in tar.getmembers():
     f=tar.extractfile(member)
     content = f.read()
     Data = np.loadtxt(content)

错误如下:

Traceback (most recent call last):
  File "dataExtPlot.py", line 21, in <module>
    content = f.read()
AttributeError: 'NoneType' object has no attribute 'read'

还有其他方法可以完成这个任务吗?


1
类似于 http://stackoverflow.com/q/33113600/1240268 - Andy Hayden
1
如果成员不属于上述任何一种类型,则返回None。该成员既不是文件也不是链接。 - Martijn Pieters
7个回答

48

文档告诉我们,如果成员不是常规文件或链接,则extractfile()会返回 None

一个可能的解决方案是跳过 None 结果:

tar = tarfile.open("filename.tar.gz", "r:gz")
for member in tar.getmembers():
     f = tar.extractfile(member)
     if f is not None:
         content = f.read()

1
在完成后不要忘记使用 tar.close()。或者更好的方法是,将其用作 with tarfile.open(...): ... - Joren

6

tarfile.extractfile() 如果成员既不是文件也不是链接,则可能返回None。例如,您的tar归档文件可能包含目录或设备文件。要解决此问题:

import tarfile
import numpy as np 

tar = tarfile.open("filename.tar.gz", "r:gz")
for member in tar.getmembers():
     f = tar.extractfile(member)
     if f:
         content = f.read()
         Data = np.loadtxt(content)

3
你可以尝试这个。
t = tarfile.open("filename.gz", "r")
for filename in t.getnames():
    try:
        f = t.extractfile(filename)
        Data = f.read()
        print filename, ':', Data
    except :
        print 'ERROR: Did not find %s in tar archive' % filename

感谢您提供的代码片段。不过您读取了两次 - 一次是在设置“data”变量时,另一次是在打印时。您能否更改代码以解决这个问题? - Saurabh Hirani

2
我的需求:
1. Python3. 2. 我的tar.gz文件由多个utf-8文本文件和目录组成。 3. 需要从所有文件中读取文本行。
问题:
1. tar.getmembers()返回的tar对象可能为None。 2. extractfile(fname)返回的内容是一个字节字符串(例如b'Hello\t\xe4\xbd\xa0\xe5\xa5\xbd')。Unicode字符无法正确显示。
解决方案:
1. 首先检查tar对象的类型。我引用了tarfile库中doc的示例。(搜索“如何读取gzip压缩的tar档案并显示一些成员信息”) 2. 从字节字符串解码为普通字符串。(ref - 最高票答案)
代码:
with tarfile.open("sample.tar.gz", "r:gz") as tar:
for tarinfo in tar:
    logger.info(f"{tarinfo.name} is {tarinfo.size} bytes in size and is: ")
    if tarinfo.isreg():
        logger.info(f"Is regular file: {tarinfo.name}")
        f = tar.extractfile(tarinfo.name)  
        # To get the str instead of bytes str
        # Decode with proper coding, e.g. utf-8
        content = f.read().decode('utf-8', errors='ignore')
        # Split the long str into lines
        # Specify your line-sep: e.g. \n
        lines = content.split('\n')
        for i, line in enumerate(lines):
            print(f"[{i}]: {line}\n")
    elif tarinfo.isdir():
        logger.info(f"Is dir: {tarinfo.name}")
    else:
        logger.info(f"Is something else: {tarinfo.name}.")

使用 content = codecs.getreader("utf-8")(f) 将字节流转换为字符串流!来源:https://dev59.com/AFsW5IYBdhLWcg3w7KxL#34511829 - dlazesz

1

您无法“读取”某些特殊文件(例如链接),但tar支持它们,tarfile可以正确提取它们。当tarfile提取它们时,它不会返回类似文件的对象,而是返回None。因此,如果您的tarball包含这样的特殊文件,则会出现错误。

一种方法是在提取之前确定您正在处理的tarball中条目的类型:有了这些信息,您可以决定是否可以“读取”该文件。您可以通过调用tarfile.getmembers()来实现这一点,该函数返回tarfile.TarInfo,其中包含有关tarball中包含的文件类型的详细信息。

tarfile.TarInfo类具有您需要确定tar成员类型的所有属性和方法,例如isfile()isdir()tinfo.islnk()tinfo.issym(),然后相应地决定如何处理每个成员(提取或不提取等)。

例如,我使用这些来测试这个修补后的tar文件中文件类型,以跳过提取特殊文件并以特殊方式处理链接:
for tinfo in tar.getmembers():
    is_special = not (tinfo.isfile() or tinfo.isdir()
                      or tinfo.islnk() or tinfo.issym())
...

0
只是为了帮助大家,我发现之前的解决方案对我来说无法将文件.tar.gz转换为文本,所以这里有一个解决方案,可以将文件.tar.gz读取为文本,然后你可以在Python中进行处理。
tar = tarfile.open(tar_archive, 'r:gz')
files = tar.getmembers()
seqs_file = tar.extractfile(files[0])
seqs_file = gzip.open(seqs_file, 'rt')
content = seqs_file.readlines()

我认为,如果你有更多的文件,你可以循环并使用gzip.open逐个打开它们,但我没有测试过。我希望这个解决方案对其他人有用。

-1
在Jupyter Notebook中,您可以像下面这样操作。
!wget -c http://qwone.com/~jason/20Newsgroups/20news-bydate.tar.gz -O - | tar -xz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接