我正在努力解决一项特别令人沮丧的任务。
我在一个目录中有一组数千个文件,比如/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'
可能不存在,因此如果发生这种情况,我需要跳过该基本名称。
我的目的是为同步数据加载创建一个文件列表。
我怎样才能有效地做到这一点?
答案 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