【问题标题】:Tarfile/Zipfile extractall() changing filename of some filesTarfile/Zipfile extractall() 更改某些文件的文件名
【发布时间】:2019-03-25 14:23:55
【问题描述】:

您好,我目前正在开发一个必须提取一些 .tar 文件的工具。

它在大多数情况下都很好用,但我有一个问题:

某些 .tar 和 .zip 文件的名称包含“非法”字符(f.ex“:”)。 这个程序必须在 windows 机器上运行,所以我必须处理这个。

如果提取的输出中包含“:”或其他非法 Windows 字符,我是否可以更改某些文件的名称。

我目前的实现:

def read_zip(filepath, extractpath):
    with zipfile.ZipFile(filepath, 'r') as zfile:
        contains_bad_char = False
        for finfo in zfile.infolist():
            if ":" in finfo.filename:
                contains_bad_char = True
        if not contains_bad_char:
            zfile.extractall(path=extractpath)


def read_tar(filepath, extractpath):
    with tarfile.open(filepath, "r:gz") as tar:
        contains_bad_char = False
        for member in tar.getmembers():
            if ":" in member.name:
                contains_bad_char = True
        if not contains_bad_char:
            tar.extractall(path=extractpath)

所以目前我只是忽略了这些输出,这并不理想。

为了更好地描述我的要求,我可以提供一个小例子:

file_with_files.tar -> small_file_1.txt
                    -> small_file_2.txt
                    -> annoying:file_1.txt
                    -> annoying:file_1.txt

应该提取到

file_with_files -> small_file_1.txt
                -> small_file_2.txt
                -> annoying_file_1.txt
                -> annoying_file_1.txt

是迭代压缩文件中的每个文件对象并逐个提取的唯一解决方案,还是有更优雅的解决方案?

【问题讨论】:

  • 您需要使用一个库,该库允许您提供一个回调函数,该函数将动态替换每个 ':' 为 '_'。例如,Zip-Ada 可以做到这一点,请参阅 Compose_func @unzip-ada.sf.net/za_html/unzip__ads.htm#85_8

标签: python file zip tar


【解决方案1】:

根据[Python.Docs]: ZipFile.extract(member, path=None, pwd=None)

在 Windows 上将非法字符(:<>|"?*)替换为下划线(_)。

所以,事情已经处理好了:

>>> import os
>>> import zipfile
>>>
>>> os.getcwd()
'e:\\Work\\Dev\\StackOverflow\\q055340013'
>>> os.listdir()
['arch.zip']
>>>
>>> zf = zipfile.ZipFile("arch.zip")
>>> zf.namelist()
['file0.txt', 'file:1.txt']
>>> zf.extractall()
>>> zf.close()
>>>
>>> os.listdir()
['arch.zip', 'file0.txt', 'file_1.txt']

快速浏览 TarFile(source 和 doc)并没有发现任何类似的东西(如果没有,我也不会很惊讶,因为 .tar 格式主要用于Nix),所以你必须手动完成。事情并不像我预期的那么简单,因为 TarFile 不像 ZipFile 那样提供以不同名称提取成员的可能性。
无论如何,这里有一段代码(我有 ZipFileTarFile 作为灵感来源):

code00.py

#!/usr/bin/env python

import sys
import os
import tarfile


def unpack_tar(filepath, extractpath=".", compression_flag="*"):
    win_illegal = ':<>|"?*'
    table = str.maketrans(win_illegal, '_' * len(win_illegal))
    with tarfile.open(filepath, "r:" + compression_flag) as tar:
        for member in tar.getmembers():
            #print(member, member.isdir(), member.name, member.path)
            #print(type(member))
            if member.isdir():
                os.makedirs(member.path.translate(table), exist_ok=True)
            else:
                with open(os.path.join(extractpath, member.path.translate(table)), "wb") as fout:
                    fout.write(tarfile.ExFileObject(tar, member).read())


def main(*argv):
    unpack_tar("arch00.tar")


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

请注意,上面的代码适用于简单的 .tar 文件(具有简单的成员,包括目录)。

已提交[Python.Bugs]: tarfile: handling Windows (path) illegal characters in archive member names
我不知道它的结果会是什么,因为我提交了几个更严重的问题(以及对它们的修复)(在我的 PoV 上),但对于各种原因,他们被拒绝了。

【讨论】:

  • 您的实现适用于所述的简单文件。但是,如果 .tar 文件包含名称带有非法字符的文件夹,则它不起作用。由于我没有在我的问题中说明任何有关此类情况的信息,因此我将这个答案标记为正确。
  • 现在呢?
  • 是的,它工作得更好,我喜欢使用 member.isdir()!
  • 您可以使用tar.extractfile + shutil.copyfileobj 来简化代码,另请参见stackoverflow.com/a/6149487
  • 在else语句中,在创建文件之前,有时必须先创建文件夹,否则会报错,加os.makedirs(pathlib.Path(extractpath).parent, exist_ok=True)
猜你喜欢
  • 2021-07-22
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 2021-07-21
  • 1970-01-01
  • 2013-03-14
  • 1970-01-01
  • 2011-05-14
相关资源
最近更新 更多