【问题标题】:Iterate within directory to zip files with python在目录中迭代以使用 python 压缩文件
【发布时间】:2011-05-06 19:24:49
【问题描述】:

我需要遍历一个文件夹并找到文件名相同的每个实例(扩展名除外),然后将它们中的每一个压缩(最好使用 tarfile)到一个文件中。

所以我有 5 个名为“example1”的文件,每个文件都有不同的文件扩展名。我需要将它们压缩在一起并将它们输出为“example1.tar”或类似的东西。

使用简单的 for 循环就很容易了,例如:

tar = tarfile.open('example1.tar',"w")

用于 glob 中的输出('example1*'):

tar.add(输出)

tar.close()

但是,有 300 个“示例”文件,我需要遍历每个文件及其关联的 5 个文件才能完成这项工作。这超出了我的想象。非常感谢任何建议。

【问题讨论】:

    标签: python loops


    【解决方案1】:

    您所描述的模式可以推广到 MapReduce。我在网上找到了 MapReduce 的a simple implementation,其中一个更简单的版本是:

    def map_reduce(data, mapper, reducer):
        d = {}
        for elem in data:
            key, value = mapper(elem)
            d.setdefault(key, []).append(value)
        for key, grp in d.items():
            d[key] = reducer(key, grp)
        return d
    

    您想按名称对所有文件进行分组,不带扩展名,您可以从os.path.splitext(fname)[0] 获得扩展名。然后,您想使用 tarfile 模块从每个组中制作一个 tarball。在代码中,即:

    import os
    import tarfile
    
    def make_tar(basename, files):
        tar = tarfile.open(basename + '.tar', 'w')
        for f in files:
            tar.add(f)
        tar.close()
    
    map_reduce(os.listdir('.'),
               lambda x: (os.path.splitext(x)[0], x),
               make_tar)
    

    编辑:如果你想以不同的方式对文件进行分组,你只需要将第二个参数修改为map_reduce。上面的代码对表达式os.path.splitext(x)[0] 具有相同值的文件进行分组。因此,要通过 all 去除扩展名的基本文件名进行分组,您可以将该表达式替换为 strip_all_ext(x) 并添加:

    def strip_all_ext(path):
        head, tail = os.path.split(path)
        basename = tail.split(os.extsep)[0]
        return os.path.join(head, basename)
    

    【讨论】:

    • 无论如何要更改此代码或使用 os.path.extsep 将多个扩展名从一个文件中拆分出来。例如'foobar.aux.xml'
    【解决方案2】:

    你可以这样做:

    • 列出目录中的所有文件
    • 创建一个字典,其中基本名称是键,所有扩展都是值
    • 然后按字典键tar所有文件

    类似这样的:

    import os
    import tarfile
    from collections import defaultdict
    
    myfiles = os.listdir(".")   # List of all files
    totar = defaultdict(list)
    
    # now fill the defaultdict with entries; basename as keys, extensions as values
    for name in myfiles:
        base, ext = os.path.splitext(name)
        totar[base].append(ext)
    
    # iterate through all the basenames
    for base in totar:
        files = [base+ext for ext in totar[base]]
        # now tar all the files in the list "files"
        tar = tarfile.open(base+".tar", "w")
        for item in files:    
            tar.add(item)
        tar.close()
    

    【讨论】:

      【解决方案3】:

      你必须解决问题。分别解决。

      1. 查找匹配的名称。使用collections.defaultict

      2. 在找到匹配的名称后创建 tar 文件。你已经得到了很好的覆盖。

      所以。先解决问题1。

      使用glob 获取所有名称。使用os.path.basename 拆分路径和基本名称。使用os.path.splitext 拆分名称和扩展名。

      列表字典可用于保存所有同名文件。

      这是你在第 1 部分中所做的吗?


      第 2 部分是将文件放入 tar 存档。为此,您已经获得了所需的大部分代码。

      【讨论】:

        【解决方案4】:

        尝试使用 glob 模块:http://docs.python.org/library/glob.html

        【讨论】:

          【解决方案5】:
          #! /usr/bin/env python
          
          import os
          import tarfile
          
          tarfiles = {}
          for f in os.listdir ('files'):
              prefix = f [:f.rfind ('.') ]
              if prefix in tarfiles: tarfiles [prefix] += [f]
              else: tarfiles [prefix] = [f]
          
          for k, v in tarfiles.items ():
              tf = tarfile.open ('%s.tar.gz' % k, 'w:gz')
              for f in v: tf.addfile (tarfile.TarInfo (f), file ('files/%s' % f) )
              tf.close ()
          

          【讨论】:

          • @Hyperboreus: -1 ... f = 'fubar'; prefix = f [:f.rfind ('.') ] 产生 'fuba' ... 使用 os.path.splitext()
          • @Hyboreus:当你在做的时候,丢掉切片和字典访问中[ 和函数调用中( 之前的丑陋空格
          • @Hyperboreus:-感谢您的帮助。使用上面的代码时,我最终得到了每个文件的 .tar 而不是每个唯一的文件名?想法? @John Machin:不确定您的 os.path.splitext() 参考。
          • @KennyC:这完全是关于使用 os.path.splitext() 从路径末尾删除扩展名(如果有的话),这是正确的做法,被 3 使用的答案。如果没有扩展,它将原样返回输入。但是@Hyboreus 使用的噱头代码失败了;它会删除最后一个字符(fubar -> fuba)。
          • @KennyC:不考虑没有句点的文件名(我的错,但其他人已经指出如何正确执行此操作),脚本打包 tar.gz 文件,按那里的名称对文件进行分组。这是一个示例输出:
          【解决方案6】:
          import os
          import tarfile
          
          allfiles = {}
          
          for filename in os.listdir("."):
              basename = '.'.join (filename.split(".")[:-1] )
              if not basename in all_files:
                  allfiles[basename] = [filename]
              else:
                  allfiles[basename].append(filename)
          
          for basename, filenames in allfiles.items():
              if len(filenames) < 2:
                  continue
              tardata = tarfile.open(basename+".tar", "w")
              for filename in filenames:
                  tardata.add(filename)
              tardata.close()
          

          【讨论】:

          • -1 Use os.path.splitext() -- '.'.join ('fubar'.split(".")[:-1]) 产生一个空字符串。
          猜你喜欢
          • 1970-01-01
          • 2012-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-18
          相关资源
          最近更新 更多