扫描多个序列文件

2024-10-01 05:06:48 发布

您现在位置:Python中文网/ 问答频道 /正文

文件结构如下所示:

folder1
    |-----name0000.jpg
    |-----name0000.tif
    |-----name0001.jpg
    |-----name0001.tif
    |-----....   
    |-----....
    |-----name2000.jpg
    |-----name2000.tif
    |-----name2004.tif
    |-----....
    |-----name2845.tif
    |-----other_file.txt
    |-----folder2
                 |-----name0000.jpg
                 |-----name0000.tif
                 |-----name0001.jpg
                 |-----name0001.tif
                 |-----....   
                 |-----....
                 |-----name2000.jpg
                 |-----name2000.tif
                 |-----other_file2.sh

我怎么能把他们分成这样的小组?在

^{pr2}$

总的文件可能有几万,我想要速度。不仅有jpg和tif文件,还可以是其他格式。在


Tags: 文件txt结构file2filejpgothertif
3条回答

使用^{}在树上行走。因为这没有给出文件大小,所以需要对每个文件调用^{}

接下来,显然要先按扩展名分组,然后按基文件名分组(如果两个文件名之间的唯一区别是某个数字部分被1分开,则两个文件名放在一起),但按文件名对组进行排序。一般来说,对事物进行分组的最简单的方法是对它们进行排序,然后通过^{}函数按邻接进行分组,之后您总是可以对它们进行排序。

我不知道你实际的分组键应该是什么,因为我想不出任何合理的方法来区分2004和0001-2000,但不能把它和2501分开。同样,我也不确定哪条规则会给你2004-2845,尽管有差距。所以我把这些部分留给你。

所以:

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函数,如下所示:

^{pr2}$

然后您可以使用排序方法中描述的相同的^{}函数:

for key, group in itertools.groupby(files, key=cmp_to_key(keycap)):

不管怎样做,到目前为止最慢的部分可能是对每个文件调用stat。这很可惜,因为os.walk可能已经有了统计信息,但它永远不会给你。

为了优化这一点,您可以直接使用本机api,以尽可能高效地为您提供信息。大多数现代的*nix平台(包括OS X和非古代linux)都有^{},这类似于用C实现的增强的{},它可以选择性地为您统计所有文件。旧的*nixe应该至少有nftw或{}。Windows有^{},这更像是一个增强的os.listdir-它可以很快地提供每个文件的所有类型的信息,包括大小,但是它不会递归到子目录中,所以您必须手动执行。


如果您的比较应该使key0000.jpgkey0001.jpg相同,但不是{}和{}或{}和{},那么显然我们需要将每个名称分解为多个部分。中间的一个需要转换成一个数字,这样0009和0010`将是相邻的(因为它们显然不是字符串)。我想你想要的是这个:*

^{4}$

因此,例如,key0000.jpg将分解为'key'0000,和{}。使用这个函数,并确保它做你真正想要的。

下一步,我们如何使用它作为比较函数?好吧,这几乎是一个正常的词典比较,除了在中间位,如果左位比右位少1,它就等于。所以:

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/0/nonzero……但它同样容易,而且可能更易读。)

同样,对不同的文件名对调用keycmp,以确保它们按您的要求运行。

你可能需要一些错误处理。作为标准,re.match不能匹配,因为你给了它,比如说,'files.txt',你会得到一个AttributeError: 'NoneType' has no attribute 'groups'。但你应该能弄明白。

最后一件事:我不记得groupby是否对照组中的last值检查每个新值,还是第一个。如果是后者,这个keyfunc就不起作用了。您可以尝试编写一个有状态的比较器,但是有一个更简单的解决方案:^{}提供了等效的Python源代码,而且没有那么复杂,所以您只需复制它并将其粘贴到代码中,然后更改它以记住组中最新的值。

最后,如果你觉得迭代器和groupby之类的东西听上去都像希腊语,那么在代码正常工作之前不要试着去做。Generator Tricks for System Programmers会教你希腊语,所有像这样的问题都是e在你的余生里更爱你。(好吧,除非你被迫用另一种没有生成器的语言写作…)


*我确信您不需要int(number, 10),因为Python2.7和3.x不会将int('0123')解释为八进制的……但是由于我必须查找它以确保它的可读性,使其显式似乎是一个好主意。在

@abarnert:它从您的博客中派生出以下代码:分组为相邻值的运行(链接:http://stupidpythonideas.blogspot.com/2014/01/grouping-into-runs-of-adjacent-values.html

我尝试了Win7中的python2.6.6和centos6.5中的python2.6.6,问题是一样的。因为没有itertools.cmp_到\u键()在这个Python2.6中,我修改了您的代码,希望问题不是来自我的修订。在

def adjacent_cmp(x, y):
    if x+1 < y: return -1
    elif x > y: return 1
    else: return 0

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K:
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

class groupby:
    # [k for k, g in groupby('AAAABBBCCDAABBB')]  > A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')]  > AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.sentinel = self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def next(self):
        while (self.currkey is self.sentinel
               or self.tgtkey is not self.sentinel
               and self.tgtkey == self.currkey):
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey))
    def _grouper(self, tgtkey):
        while tgtkey is self.sentinel or tgtkey == self.currkey:
            yield self.currvalue
            self.currvalue = next(self.it)    # Exit on StopIteration
            tgtkey, self.currkey = self.currkey, self.keyfunc(self.currvalue)

adjacent_key = cmp_to_key(adjacent_cmp)
a = [0, 1, 2]
print [list(g) for k, g in groupby(a, adjacent_key)]

[[0, 1, 2], [2]]

大部分的工作是将文件大小转换为人类可读的格式。看看这对你有没有用

import os

def sizify(fpath):
    bytes = os.stat(fpath).st_size
    suff = 0
    while b//1000:
        b = b//1000
        suff += 1
    return str(b) + ["B", "MB", "GB" "TB"][suff]

def humanReadable(bytes):
    suff = 0
    while b//1000:
        b = b//1000
        suff += 1
    return str(b) + ["B", "MB", "GB" "TB"][suff]    

def getRuns(fnames):
    fnames.sort()
    answer = []
    start = fnames[0]
    for mid,high in zip(fnames, fnames[1:]):
        mid = int(mid.rsplit('.')[0].lstrip('name'))
        high = int(high.rsplit('.')[0].lstrip('name'))
        if high-mid > 1:
            answer.append((start, mid, 
                          sum(os.stat("name%s.jpg" %i).st_size for i in range(start, mid+1)) +
                          sum(os.stat("name%s.tiff" %i).st_size for i in range(start, mid+1))))
            start = high
    answer.append((start, mid, 
                          sum(os.stat("name%s.jpg" %i).st_size for i in range(start, mid+1)) +
                          sum(os.stat("name%s.tiff" %i).st_size for i in range(start, mid+1))))
    return answer

def main():
    for dir, dirs, files in os.walk('folder1'):
        runs = getRuns(files)
        for low,high,size in runs:
            print("%s: name%s-%s, %s" %(dir, low, high, humanReadable(size)))

请注意,这将处理1KB=1000B而不是1KB=1024B
因此,根据您所在的系统,您可能需要考虑更改它。在

相关问题 更多 >