Python:使用相同的basename对来自不同目录的文件进行分组

时间:2017-10-28 00:24:14

标签: python string file

我正在努力解决一项特别令人沮丧的任务。

我在一个目录中有一组数千个文件,比如/path/to/file#####.txt。在其他目录中,我有(可能是相同数量的)具有相同基本名称但后缀不同的文件,例如/diff/path/to/file#####.txt.foo

我正在尝试将这些文件组合在一起,以便我有一个列表列表

[['/path/to/file#####.txt', '/diff/path/to/file#####.txt.foo', 
  '/another/path/to/file#####.txt.bar'], ...]

可能,但不保证每个子转发器中都有相应的文件,但不能保证。换句话说,'/path/to/file#####.txt'可能存在但'/diff/path/to/file#####.txt.foo'可能不存在,因此如果发生这种情况,我需要跳过该基本名称。

我的目的是为同步数据加载创建一个文件列表。

我怎样才能有效地做到这一点?

2 个答案:

答案 0 :(得分:1)

我最终提出了一个效率相当高的解决方案,但它似乎并不是最优雅的。

基本上,我首先找到所有文件基名并形成所有可能集的列表列表,例如。

groups = [['/path/to/file00000.txt', '/diff/path/to/file00000.txt.foo', 
  '/another/path/to/file00000.txt.bar'],
  ['/path/to/file00001.txt', '/diff/path/to/file00001.txt.foo', 
  '/another/path/to/file00001.txt.bar'], ...]

然后我使用os.path.exists()检查每个目录中是否存在具有给定基本名称的文件,例如

del_idx = []
for i in xrange(len(groups)):
    for j in len(groups[i]):
        if not os.path.exists(groups[i][j]):
            del_idx.append(i)
            continue # Because if one doesn't exist, no need to check others

现在我有一个"坏"的索引列表,我只是反过来循环删除它们。

for i in xrange(len(del_idx)-1,-1,-1):
    groups.pop(del_idx[i])

在我只有3元组的情况下这很好用,但如果元组中有很多路径,这可能会崩溃。

对于约260k文件,所有组构造花费约12秒,存在检查花费约35秒,删除花费约12秒。这是相当合理的,但是,对于m个文件和大小为n的组,此算法再次为O(m * n),因此如果组大小变大,则该算法不理想。

答案 1 :(得分:1)

我建议的解决方案:

import glob
import os.path as op
from collections import defaultdict
def variable_part(file, base, ext):
    return file[len(base):-len(ext)-1]
def func(dirs):
    base = 'file'
    files = defaultdict(list)
    dirext = []
    for d in dirs:
        local_files = glob.glob(op.join(d, '*'))
        local_ext = '.'.join(local_files[0].split('.')[1:])
        for f in local_files:
            files[variable_part(op.basename(f), base, local_ext)].append(f)
    return list(files.values())

尚未对其进行分析,但我的感觉是它接近最优,每个文件名都被处理一次,并且在第一个目录之后,对files的任何访问都应该已经摊销了。一些额外的优化肯定是可能的,特别是在处理字符串时。

如果变量部分只是从0到M-1的整数,那么如果你有N个目录,那么最好有一系列长度为N的M列表X_k;根据第i个目录中是否存在文件文件 k .xxx,将每个X_k [i]设置为1或0。只有这样你才能生成最终的文件名列表,无需删除(正如您可能已经注意到的那样,这对于列表来说是一项昂贵的操作)。

在任何情况下,此算法的最低复杂度为N * M,绝不会让您远离每个目录并查看所有文件;那些35秒可以通过单个系统调用进行优化以获取所有目录,然后在内存中工作,但这不会改变整体复杂性,即算法如何扩展。

修改 我对此有点好奇,我做了一个测试。实际上,显然处理由glob检索的文件名似乎比检查每个文件是否存在更快(至少在我的mac HFS +文件系统上,在ssd上)。

In [0]: def x():
     ...:     return [os.path.exists('test1/file%06d.txt.gz' % i) for i in range(10000)]
     ...:

In [1]: def y():
     ...:     ff = glob.glob('test1/*')
     ...:     res = [False]*10000
     ...:     for s in ff:
     ...:         res[int(s[10:16])] = True
     ...:     return res
     ...:

In [2]: %timeit x()
10 loops, best of 3: 71.2 ms per loop

In [3]: %timeit y()
10 loops, best of 3: 32.6 ms per loop