2011-03-18 69 views
26

考虑下面的Python代码过滤文件?在这种特殊情况下,我想要以* .png,*。gif,* .jpg或* .jpeg结尾的所有文件。使用fnmatch.filter由多于一个可能的文件扩展名

现在我来到了

for root, dirs, files in os.walk(directory): 
    for extension in ['jpg', 'jpeg', 'gif', 'png']: 
     for filename in fnmatch.filter(files, '*.' + extension): 
      pass 

但我认为这是不是很优雅和高性能。

有人有更好的主意吗?

+0

应该是好的,但一个'tuple'会在这里更好的(挑剔!)'( 'JPG', 'JPEG', 'GIF', 'PNG')'。 – user225312 2011-03-18 12:12:18

+4

@A A:为什么在这种情况下元组更好? – tyrondis 2011-03-18 12:16:37

回答

35

如果你只需要检查的扩展(即没有进一步的通配符),你为什么不干脆用基本的字符串操作?

for root, dirs, files in os.walk(directory): 
    for filename in files: 
     if filename.endswith(('.jpg', '.jpeg', '.gif', '.png')): 
      pass 
+0

谢谢 - 这是一个相当明显的解决方案 - 但我没有看到它=) – tyrondis 2011-03-18 12:44:30

+4

请注意,这种方法是区分大小写的,例如名为'annoying.GIF文件'不会被包含在上面的例子中。 这可以通过标准化文件名来解决:'... if filename.lower()。endswith(('.jpg','.jpeg','.gif','.png'))...' – asherbar 2016-04-25 06:03:27

3

这不是真正的优雅下去,但它的工作原理:

for root, dirs, files in os.walk(directory): 
    for filename in fnmatch.filter(files, '*.png') + fnmatch.filter(files, '*.jpg') + fnmatch.filter(files, '*.jpeg') + fnmatch.filter(files, '*.gif'): 
     pass 
3

这将是一个更好的办法,也许是因为你没有打电话+反复使用tuple代替list

for root, dirs, files in os.walk(directory): 
    for extension in ('*.jpg', '*.jpeg', '*.gif', '*.png'): 
     for filename in fnmatch.filter(files, extension): 
      pass 

A tuple更好,因为您创建它们后不打算修改扩展名。你只是用它来遍历它们。

8

我认为你的代码其实很好。如果你想摸摸每名只有一次,定义自己的过滤功能:

def is_image_file(filename, extensions=['.jpg', '.jpeg', '.gif', '.png']): 
    return any(filename.endswith(e) for e in extensions) 

for root, dirs, files in os.walk(directory): 
    for filename in filter(is_image_file, files): 
     pass 
+0

你确定这会有所作为吗?为什么不定义一个元组(它只会被创建一次,对吧?)而不用担心这一切? (只是问!) – user225312 2011-03-18 12:21:20

+0

@AA:在你的代码中,元组将在外循环的每次迭代中创建。在我的代码中,列表只会创建一次。我不认为这里的性能差异是相关的。我的建议更多地是关于代码可读性 - 你的也很好。 – 2011-03-18 12:25:22

+0

真的。我只是想确定。 – user225312 2011-03-18 12:25:59

6

我一直在使用它,并取得了很多成功。

import fnmatch 
import functools 
import itertools 
import os 

# Remove the annotations if you're not on Python3 
def find_files(dir_path: str=None, patterns: [str]=None) -> [str]: 
    """ 
    Returns a generator yielding files matching the given patterns 
    :type dir_path: str 
    :type patterns: [str] 
    :rtype : [str] 
    :param dir_path: Directory to search for files/directories under. Defaults to current dir. 
    :param patterns: Patterns of files to search for. Defaults to ["*"]. Example: ["*.json", "*.xml"] 
    """ 
    path = dir_path or "." 
    path_patterns = patterns or ["*"] 

    for root_dir, dir_names, file_names in os.walk(path): 
     filter_partial = functools.partial(fnmatch.filter, file_names) 

     for file_name in itertools.chain(*map(filter_partial, path_patterns)): 
      yield os.path.join(root_dir, file_name) 

实例:

for f in find_files(test_directory): 
    print(f) 

收率:

.\test.json 
.\test.xml 
.\test.ini 
.\test_helpers.py 
.\__init__.py 

测试使用多种模式:

for f in find_files(test_directory, ["*.xml", "*.json", "*.ini"]): 
    print(f) 

收率:

.\test.json 
.\test.xml 
.\test.ini 
0

这是我用来过滤apache日志目录中的文件。 在这里,我排除错误flles

rep_filters = [now.strftime("%Y%m%d")] 
def files_filter(liste_fic, filters = rep_filters): 
    s = "(fic for fic in liste_fic if fic.find('error') < 0" 
    for filter in filters: 
     s += " and fic.find('%s') >=0 " % filter 
    s += ")" 
    return eval(s)