【问题标题】:Get a filtered list of files in a directory获取目录中文件的过滤列表
【发布时间】:2011-01-14 14:19:44
【问题描述】:

我正在尝试使用 Python 获取目录中的文件列表,但我不想要所有文件的列表。

我本质上想要的是能够执行以下操作,但使用 Python 而不是执行 ls。

ls 145592*.jpg

如果没有为此的内置方法,我目前正在考虑编写一个 for 循环来遍历 os.listdir() 的结果并将所有匹配的文件附加到一个新列表中。

但是,该目录中有很多文件,因此我希望有一种更有效的方法(或内置方法)。

【问题讨论】:

  • [此链接可能对您有所帮助 :) 获取目录中文件的过滤列表](codereview.stackexchange.com/a/33642)
  • 请注意,如果排序顺序对您的应用程序很重要,您可能会特别注意排序顺序。

标签: python filesystems wildcard glob directory-listing


【解决方案1】:

您可以定义模式并检查它。在这里,我采用了开始和结束模式并在文件名中查找它们。 FILES 包含目录中所有文件的列表。

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE.startwith(PATTERN_START) and PATTERN_END in FILE.endswith(PATTERN_END):
            print FILE

【讨论】:

  • PATTERN_START 应用作 FILE.startwith(PATTERN_START)PATTERN_END 应用作 FILE.endswith(PATTERN_END) 以避免任何其他文件名组合。例如上面的代码也将允许jpg_sample_145592 文件。这是不正确的。
  • 我觉得应该是if FILE.startwith(PATTERN_START) and FILE.endswith(PATTERN_END):
【解决方案2】:
import glob

jpgFilenamesList = glob.glob('145592*.jpg')

在 python 文档中查看glob

【讨论】:

  • 哦,我刚刚注意到 Python 文档说 glob() “是通过同时使用 os.listdir() 和 fnmatch.fnmatch() 函数完成的,而不是通过实际调用子shell” .换句话说,glob() 没有预期的效率提升。
  • 有一个主要区别:glob.glob('145592*.jpg') 打印文件的整个绝对路径,而 ls 145592*.jpg 仅打印文件列表。
  • @Ben 为什么调用子shell(子进程)会提高效率?
  • @PauloNeves:是的,我上面的评论在 7 年后对我来说也没有意义。 :-) 我猜我指的是glob() 只使用listdir+fnmatch,而不是特殊的操作系统调用来进行通配符过滤。例如,在 Windows 上,FindFirstFile API 允许您指定通配符,以便操作系统直接进行过滤,并且可能更有效(我认为 Linux 上没有等效项)。
  • 别忘了使用import glob
【解决方案3】:

您可以使用 Python 标准库 3.4 及更高版本中提供的pathlib

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

【讨论】:

  • 或者,只需使用 Path.cwd().glob("145592*.jpg")... 无论如何,这在此页面上肯定应该更高。 pathlib 是要走的路
【解决方案4】:

使用glob 模块过滤:

导入全局

import glob

通配符:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

过滤器扩展.txt:

files = glob.glob("/home/ach/*/*.txt")

单个字符

glob.glob("/home/ach/file?.txt")

数字范围

glob.glob("/home/ach/*[0-9]*")

字母范围

glob.glob("/home/ach/[a-c]*")

【讨论】:

    【解决方案5】:

    保持简单:

    import os
    relevant_path = "[path to folder]"
    included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
    file_names = [fn for fn in os.listdir(relevant_path)
                  if any(fn.endswith(ext) for ext in included_extensions)]
    

    我更喜欢这种形式的列表推导,因为它的英文读起来很好。

    我将第四行读为: 对于我的路径的 os.listdir 中的每个 fn,只给我与我包含的任何一个扩展匹配的那些。

    Python 新手程序员可能很难真正习惯使用列表推导进行过滤,并且对于非常大的数据集可能会有一些内存开销,但对于列出目录和其他简单的字符串过滤任务,列表推导领先更干净的可记录代码。

    这个设计唯一的一点是它不能保护你避免犯下传递字符串而不是列表的错误。例如,如果您不小心将字符串转换为列表并最终检查字符串的所有字符,您最终可能会得到大量误报。

    但最好有一个容易解决的问题,而不是一个难以理解的解决方案。

    【讨论】:

    • 这里不需要any(),因为str.endswith()有一个序列的结尾。 if fn.endswith(included_extentensions) 绰绰有余。
    • 除了Martijn指出的不使用str.endswith(seq)的低效率之外,这是不正确的,因为文件必须以.ext结尾才能具有该扩展名。此代码还将找到(例如)名为“myjpg”的文件或仅名为“png”的目录。要解决此问题,只需在 included_extensions 中的每个扩展名前加上 .
    • 我总是对答案中显然没有运行或无法运行的代码有点警惕。变量included_extensionsincluded_extentsions?很遗憾,否则这是我的首选答案。
    【解决方案6】:

    初步代码

    import glob
    import fnmatch
    import pathlib
    import os
    
    pattern = '*.py'
    path = '.'
    

    解决方案 1 - 使用“glob”

    # lookup in current dir
    glob.glob(pattern)
    
    In [2]: glob.glob(pattern)
    Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']
    

    解决方案 2 - 使用“os”+“fnmatch”

    变体 2.1 - 在当前目录中查找

    # lookup in current dir
    fnmatch.filter(os.listdir(path), pattern)
    
    In [3]: fnmatch.filter(os.listdir(path), pattern)
    Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']
    

    变体 2.2 - 递归查找

    # lookup recursive
    for dirpath, dirnames, filenames in os.walk(path):
    
        if not filenames:
            continue
    
        pythonic_files = fnmatch.filter(filenames, pattern)
        if pythonic_files:
            for file in pythonic_files:
                print('{}/{}'.format(dirpath, file))
    

    结果

    ./wsgi.py
    ./manage.py
    ./tasks.py
    ./temp/temp.py
    ./apps/diaries/urls.py
    ./apps/diaries/signals.py
    ./apps/diaries/actions.py
    ./apps/diaries/querysets.py
    ./apps/library/tests/test_forms.py
    ./apps/library/migrations/0001_initial.py
    ./apps/polls/views.py
    ./apps/polls/formsets.py
    ./apps/polls/reports.py
    ./apps/polls/admin.py
    

    解决方案 3 - 使用“pathlib”

    # lookup in current dir
    path_ = pathlib.Path('.')
    tuple(path_.glob(pattern))
    
    # lookup recursive
    tuple(path_.rglob(pattern))
    

    注意事项:

    1. 在 Python 3.4 上测试
    2. 仅在 Python 3.4 中添加了模块“pathlib”
    3. Python 3.5 添加了使用 glob.glob 进行递归查找的功能 https://docs.python.org/3.5/library/glob.html#glob.glob。由于我的机器安装了 Python 3.4,因此我没有对其进行测试。

    【讨论】:

      【解决方案7】:

      “path/to/images”中带有“jpg”和“png”扩展名的文件名:

      import os
      accepted_extensions = ["jpg", "png"]
      filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]
      

      【讨论】:

      【解决方案8】:

      另一种选择:

      >>> import os, fnmatch
      >>> fnmatch.filter(os.listdir('.'), '*.py')
      ['manage.py']
      

      https://docs.python.org/3/library/fnmatch.html

      【讨论】:

      • 这正是glob 在一行中所做的。
      • 唯一的区别是glob 返回完整路径,而os.listdir 只返回文件名。至少这是 Python 2 中正在发生的事情。
      • 一个非常好的解决方案。特别是对于那些已经在他们的脚本中使用fnmatchos 并且不想导入另一个模块的人,即。 glob.
      【解决方案9】:
      import os
      
      dir="/path/to/dir"
      [x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]
      

      这将为您提供包含完整路径的 jpg 文件列表。对于文件名,您可以将 x[0]+"/"+f 替换为 f。您还可以将f.endswith(".jpg") 替换为您希望的任何字符串条件。

      【讨论】:

        【解决方案10】:

        您可以使用 subprocess.check_ouput() 作为

        import subprocess
        
        list_files = subprocess.check_output("ls 145992*.jpg", shell=True) 
        

        当然,引号之间的字符串可以是你想在shell中执行的任何东西,并存储输出。

        【讨论】:

        【解决方案11】:

        您可能还喜欢更高级的方法(我已经实现并打包为 findtools):

        from findtools.find_files import (find_files, Match)
        
        
        # Recursively find all *.txt files in **/home/**
        txt_files_pattern = Match(filetype='f', name='*.txt')
        found_files = find_files(path='/home', match=txt_files_pattern)
        
        for found_file in found_files:
            print found_file
        

        可以安装

        pip install findtools
        

        【讨论】:

          【解决方案12】:

          使用 os.walk 递归列出您的文件

          import os
          root = "/home"
          pattern = "145992"
          alist_filter = ['jpg','bmp','png','gif'] 
          path=os.path.join(root,"mydir_to_scan")
          for r,d,f in os.walk(path):
              for file in f:
                  if file[-3:] in alist_filter and pattern in file:
                      print os.path.join(root,file)
          

          【讨论】:

          • 无需切片; file.endswith(alist_filter) 就够了。
          • 我们必须使用any(file.endswith(filter) for filter in alist_filter),因为endswith()不允许列表作为参数。
          【解决方案13】:

          glob.glob() 绝对是这样做的方法(根据 Ignacio)。但是,如果您确实需要更复杂的匹配,您可以使用列表解析和re.match() 来完成,如下所示:

          files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]
          

          更灵活,但正如您所说,效率较低。

          【讨论】:

          • 这绝对看起来更强大。例如,必须做类似[0-9]+
          • 是的,绝对更强大——不过 fnmatch 确实支持[0123456789] 序列(see docs),并且它还具有fnmatch.filter() 函数,这使得这个循环更加高效。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-09-02
          • 1970-01-01
          • 2015-11-30
          • 2010-12-26
          • 1970-01-01
          • 2014-06-06
          相关资源
          最近更新 更多