从zip文件中解压选定的文件

3

我有一个zip文件,其内部文件夹结构如下:

CODE
`-- CODE
    `-- CODE
        `-- CODE
            |-- 2019
            |   |-- file1.txt
            |   `-- file2.txt
            |-- 2020
            |   `-- file3.txt
            `-- 2021
                |-- file4.txt
                `-- file5.txt

我想按照以下给定的文件夹结构解压缩文件:

CODE
|-- 2019
|   |-- file1.txt
|   `-- file2.txt
|-- 2020
|   `-- file3.txt
`-- 2021
    |-- file4.txt
    `-- file5.txt

我可以硬编码它,但由于这是一个重复的请求,我是否可以以编程方式处理,仅解压那些有文件的文件夹。

我的当前代码如下:

def unzipfiles(incoming_path):
    for path,subdirs,files in os.walk(incoming_path):
        for name in files:
            if(name.endswith('.zip')):
                with zipfile.ZipFile(os.path.join(incoming_path,name), 'r') as zip_ref:
                    for file in zip_ref.namelist():
                        out_path=os.path.join(incoming_path,file)
                        out_path=out_path.replace('CODE/','')
                        if(out_path[:-1]!=incoming_path):
                            zip_ref.extract(file,out_path)

然而,它并没有正确地工作,并且比zip文件中存在的目录创建了更多的文件夹。

如果我理解正确的话,您想要解压缩一个文件,然后删除其中只包含另一个目录的目录? - Thymen
没错。 - Abhinav Dhiman
我可以假设目录名称是重复的,或者第一个包含数据的文件夹是年份吗?换句话说,文件夹的命名是否与zip文件名和所有其他子目录一致? - Thymen
是的。我创建了一个新的函数,它对我很有效。我已经将代码粘贴在答案中。 - Abhinav Dhiman
2个回答

1
我使用的解决方案是将文件的完整路径映射为相对较短的名称。对于这个解决方案,我将采用 OP 提供的 zip 结构。
import os
import re
import pathlib
import shutil
import zipfile
from pprint import pprint

if __name__ == '__main__':
    toplevel = os.path.join('files')
    new_structure = dict()

    # Let's just extract everything
    with zipfile.ZipFile('CODE.zip', 'r') as zip_file:

        for zip_info in zip_file.infolist():
            path = pathlib.PurePath(zip_info.filename)

            # This writes the data from the old file to a new file.
            if str(path.parent) in new_structure:
                source = zip_file.open(zip_info)
                target = open(os.path.join(new_structure[str(path.parent)], path.name), "wb")

                with source, target:
                    shutil.copyfileobj(source, target)

            # Create the new folder structure mapping, based on the year name.
            # The matches are based on numbers in this example, but can be specified.
            if re.match('\d+', path.name):  
                new_structure[str(path)] = os.path.join(toplevel, path.name)
                os.makedirs(new_structure[str(path)], exist_ok=True)

    pprint(new_structure)

输出结果 (pprint) 显示了重新映射的结构:

{'CODE\\CODE\\CODE\\CODE\\2019': 'files\\2019',
 'CODE\\CODE\\CODE\\CODE\\2020': 'files\\2020',
 'CODE\\CODE\\CODE\\CODE\\2021': 'files\\2021'}

输出结果是一个具有以下结构的新文件夹:

files
|-- 2019
|   |-- file1.txt
|   `-- file2.txt
|-- 2020
|   `-- file3.txt
`-- 2021
    |-- file4.txt
    `-- file5.txt

注释

有两个有趣的点需要注意:

  1. 使用正则表达式模式匹配来确定文件路径'\d+',它只接受数字列表,如果您想更精确地匹配四位数字,可以使用\d{4}

  2. 此方法仅假定一个较低级别,换句话说,多个嵌套文件将无法正确解压缩。为此,行if str(path.parent) in new_structure:必须更改以考虑多个父路径。


0

这段代码对我来说可行。

def removeEmptyFolders(path, removeRoot=True):
  if not os.path.isdir(path):
    return
  files = os.listdir(path)
  if len(files):
    for f in files:
      fullpath = os.path.join(path, f)
      if os.path.isdir(fullpath):
        removeEmptyFolders(fullpath)
  files = os.listdir(path)
  if len(files) == 0 and removeRoot:
    os.rmdir(path)

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