使用os.walk
走树。由于这不会给你文件大小,所以你需要在每个文件上调用os.stat
。
接下来,显然你想先按扩展名进行分组,然后按基本文件名(如果两个文件名之间的唯一区别是某些数字部分关闭1,则两个文件名一起进行分组),但按文件名对文件名进行排序。一般来说,分组最简单的方法是对它们进行排序,然后通过邻接功能进行分组,然后您可以随后对其进行排序。
我不知道你的实际分组的关键应该是,因为我想不出任何明智的,将自0001-2000分离2004年,但不是从2501同样分开,我不可以肯定的是,尽管存在差距,这个规则会给你带来什么。所以我会把这些部分留给你。
所以:
def keyfunc(value):
base, ext, size = value
# FILL THIS IN
def format_group(bases):
# FILL THIS IN
def format_size(size):
# you can use inspectorG4dget's code here
for root, dirs, names in os.walk(path):
sizes = (os.stat(name).st_size for name in names)
bases, exts = zip(*map(os.path.splitext, names))
files = zip(bases, exts, sizes)
# now sort by ext, and then by base within each ext
files = sorted(files, key=operator.itemgetter(1, 0))
results = []
for key, group in itertools.groupby(files, key=keyfunc):
bases, exts, sizes = zip(*list(group))
results.append((format_group(bases), sum(size))
for base, size in sorted(results):
print('{}: {}, {}'.format(root, base, format_size(size)))
在某些情况下,没有明显的分组键功能,但有一个明显的方式来告诉两个相邻值是否算为同一组的一部分。如果是这样,写,作为一个旧式cmp
功能,如:
def keycmp(x, y):
if x should be in the same group as y:
return 0
return -1
然后你可以使用在排序HOWTO描述的相同functools.cmp_to_key
功能:
for key, group in itertools.groupby(files, key=cmp_to_key(keycap)):
但是你做这可能会证明,到目前为止,最慢的部分是在每个文件上调用stat
。这是一个耻辱,因为os.walk
可能已经有该统计信息,但它永远不会把它交给你。
优化这个,你可以直接到,作为有效给你的信息尽可能原生API。大多数现代* nix平台(包括OS X和非古Linux)的有fts
,这就好比是在C++实现的各式的os.walk
,其可任选统计所有文件给你。旧的* nixes应该至少有nftw
或ftw
。Windows有FindFirstFile
,这更像是一个加速版os.listdir
-它为每个文件提供各种信息,包括大小,速度非常快,但不会递归到子目录中,因此您必须手动执行此操作。
如果你的比较应该使key0000.jpg
和key0001.jpg
相同,但不key0000.jpg
和key0002.jpg
或key0000.jpg
和key0001.tif
,显然我们需要向下突破每名成碎片。中间一个需要转换为一个数字,这样0009
和0010`将会相邻(因为它们显然不是字符串)。我想你想要的是这样的:*
pattern = re.compile('(.*?)(\d+)(.*)')
def splitname(name):
prefix, number, suffix = pattern.match(name).groups()
return prefix, int(number, 10), suffix
因此,例如,key0000.jpg
会分解成'key'
,0000
和'.jpg'
。玩这个功能,并确保它正在做你真正想要的。
接下来,我们如何使用它来比较函数?那么,它是几乎一个正常的词典比较,除了在中间位,如果左边的一个比右边少一个,它计数相等。所以:
def keycmp(a, b):
abits, bbits = splitname(a), splitname(b)
if abits[0] < bbits[0]: return -1
elif abits[0] > bbits[0]: return 1
if abits[1]+1 < bbits[1]: return -1
elif abits[1] > bbits[1]: return 1
if abits[2] < bbits[2]: return -1
elif abits[2] > bbits[2]: return 1
else: return 0
keyfunc = functools.cmp_to_key(keycmp)
(我们实际上并不需要从旧式cmp
功能全面-1/0/1的回报,只是非零/ 0 /非零...但它一样简单,而且可能更具可读性)
再次,请拨打各种文件名对keycmp
,以确保他们正在做你想做的。
而你在这里可能会需要一些错误处理。正如它的标准,re.match
由于你给它,例如'files.txt'
而无法匹配,你会得到AttributeError: 'NoneType' has no attribute 'groups'
。但你应该能够弄清楚。
最后一两件事:我不记得,如果groupby
检查每个新值对组中的最后值或第一。如果是后者,这个keyfunc
将不起作用。您可以尝试编写一个有状态的比较器,但有一个更简单的解决方案:groupby
为您提供了等效的Python源代码,并且它不那么复杂,因此您可以将其复制并粘贴到代码中并将其更改为记住组中最近的价值。最后,如果整个迭代器和groupby等处理都听起来像是希腊语,那么不要试图在代码运行之前对它进行轰炸。 Generator Tricks for System Programmers会教你希腊语,像这样的一类问题将会在你的余生中更容易。 (好吧,直到你不得不在没有发电机另一种语言写的...)
*我相当确保你不需要int(number, 10)
,因为Python 2.7和3.x将不会将int('0123')
解释为八进制...但是由于我必须查明它的可信度,因此明确表示它似乎是一个可读性的好主意。
我不清楚你在问什么。请提供更多细节。 – CrazyCasta