【问题标题】:How can I search sub-folders using glob.glob module? [duplicate]如何使用 glob.glob 模块搜索子文件夹? [复制]
【发布时间】:2013-01-25 17:35:30
【问题描述】:

我想在一个文件夹中打开一系列子文件夹并找到一些文本文件并打印一些文本文件的行。我正在使用这个:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

但这也不能访问子文件夹。有谁知道我如何使用相同的命令来访问子文件夹?

【问题讨论】:

标签: python filesystems glob fnmatch


【解决方案1】:

(在其他答案中当然提到了第一个选项,这里的目标是表明 glob 在内部使用os.scandir,并提供直接答案)。


使用全局

如前所述,使用 Python 3.5+ 很容易:

import glob
for f in glob.glob('d:/temp/**/*', recursive=True):
    print(f)

#d:\temp\New folder
#d:\temp\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document.txt

使用路径库

from pathlib import Path
for f in Path('d:/temp').glob('**/*'):
    print(f)

使用 os.scandir

os.scandirglob 在内部所做的。所以这里是如何直接使用yield

def listpath(path):
    for f in os.scandir(path):
        f2 = os.path.join(path, f)
        if os.path.isdir(f):
            yield f2
            yield from listpath(f2)
        else:
            yield f2

for f in listpath('d:\\temp'):
    print(f)

【讨论】:

    【解决方案2】:

    您可以直接从 glob 模块中使用函数 glob.glob()glob.iglob() 从目录/文件和子目录/子文件中递归检索路径。

    语法:

    glob.glob(pathname, *, recursive=False) # pathname = '/path/to/the/directory' or subdirectory
    glob.iglob(pathname, *, recursive=False)
    

    在你的例子中,可以这样写:

    
    import glob
    import os
    
    configfiles = [f for f in glob.glob("C:/Users/sam/Desktop/*.txt")]
    
    for f in configfiles:
        print(f'Filename with path: {f}')
        print(f'Only filename: {os.path.basename(f)}')
        print(f'Filename without extensions: {os.path.splitext(os.path.basename(f))[0]}')
    

    输出:

    Filename with path: C:/Users/sam/Desktop/test_file.txt
    Only filename: test_file.txt
    Filename without extensions: test_file
    

    帮助: Documentationos.path.splitextdocumentationos.path.basename

    【讨论】:

      【解决方案3】:

      命令rglob 将无限递归到目录结构的最深子级。但是,如果您只想要一层深度,则不要使用它。

      我意识到 OP 正在谈论使用 glob.glob。然而,我相信这回答了递归搜索所有子文件夹的意图。

      rglob 函数最近使数据处理算法的速度提高了 100 倍,该算法使用文件夹结构作为数据读取顺序的固定假设。但是,使用rglob,我们能够对指定父目录下的所有文件进行一次扫描,将它们的名称保存到一个列表(超过一百万个文件),然后使用该列表来确定我们需要哪些文件仅根据文件命名约定与它们所在的文件夹在将来的任何时候打开。

      【讨论】:

        【解决方案4】:

        关于这个话题有很多困惑。让我看看我是否可以澄清它(Python 3.7):

        1. glob.glob('*.txt') :匹配当前目录中所有以'.txt'结尾的文件
        2. glob.glob('*/*.txt') :同1
        3. glob.glob('**/*.txt') :匹配仅直接子目录中所有以“.txt”结尾的文件,而不匹配当前目录中的所有文件
        4. glob.glob('*.txt',recursive=True) :同1
        5. glob.glob('*/*.txt',recursive=True) :同3
        6. glob.glob('**/*.txt',recursive=True):匹配当前目录和所有子目录中所有以“.txt”结尾的文件

        所以最好总是指定recursive=True.

        【讨论】:

          【解决方案5】:

          在 Python 3.5 和更高版本中,使用新的递归 **/ 功能:

          configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)
          

          当设置recursive 时,** 后跟路径分隔符匹配 0 个或多个子目录。

          在早期的 Python 版本中,glob.glob() 无法递归列出子目录中的文件。

          在这种情况下,我会使用 os.walk() 结合 fnmatch.filter() 来代替:

          import os
          import fnmatch
          
          path = 'C:/Users/sam/Desktop/file1'
          
          configfiles = [os.path.join(dirpath, f)
              for dirpath, dirnames, files in os.walk(path)
              for f in fnmatch.filter(files, '*.txt')]
          

          这将递归遍历您的目录并将所有绝对路径名返回到匹配的.txt 文件。在这种特定的情况下,fnmatch.filter() 可能是矫枉过正,您也可以使用.endswith() 测试:

          import os
          
          path = 'C:/Users/sam/Desktop/file1'
          
          configfiles = [os.path.join(dirpath, f)
              for dirpath, dirnames, files in os.walk(path)
              for f in files if f.endswith('.txt')]
          

          【讨论】:

          • 我可以看到:glob.glob('/path to directory/*/*.txt") 为我工作。这基本上是使用 Unix shell 规则。
          • @User123:它没有递归地列出目录。您列出了所有文本文件一层深,但没有在进一步的子目录中,甚至没有直接在path to directory 中。
          • 这并不完全相关,但为什么设置recursive=False**/ 功能不会提供给定文件夹中的文件列表,而是提供其子文件夹中的文件列表?
          • @Dr_Zaszuś:抱歉? **/ 给出了当前工作目录中目录名称 的列表,因为模式以/ 结尾,而recursive=False 基本上有一个双*,匹配与*/,只是效率较低。
          • @Dr_Zaszuś:如果您需要所有子目录中的所有文件,请使用*/*
          【解决方案6】:

          如果您运行的是 Python 3.4+,则可以使用 pathlib 模块。 Path.glob() 方法支持** 模式,这意味着“这个目录和所有子目录,递归地”。它返回一个生成器,为所有匹配的文件生成 Path 对象。

          from pathlib import Path
          configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
          

          【讨论】:

            【解决方案7】:

            在直接子目录中查找文件:

            configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')
            

            对于遍历所有子目录的递归版本,您可以使用** 并传递recursive=True since Python 3.5

            configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)
            

            两个函数调用都返回列表。您可以使用glob.iglob() 来一一返回路径。或use pathlib:

            from pathlib import Path
            
            path = Path(r'C:\Users\sam\Desktop')
            txt_files_only_subdirs = path.glob('*/*.txt')
            txt_files_all_recursively = path.rglob('*.txt') # including the current dir
            

            这两种方法都返回迭代器(您可以一一获取路径)。

            【讨论】:

            • 是的,我明白了;但我没想到glob() 也支持目录中的模式。
            • 评论已删除,我现在才知道它给人的印象是错误的;此外,该补丁还包括针对** 递归案例的文档更新。但是要让** 工作,您必须设置recursion=True 开关,顺便说一句。
            【解决方案8】:

            如果可以安装 glob2 包...

            import glob2
            filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
            folders = glob2.glob("C:\\top_directory\\**\\")
            

            所有文件名和文件夹:

            all_ff = glob2.glob("C:\\top_directory\\**\\**")  
            

            【讨论】:

              【解决方案9】:

              configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

              不适用于所有情况,而是使用 glob2

              configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
              

              【讨论】:

                【解决方案10】:

                正如 Martijn 所指出的,glob 只能通过 Python 3.5 中引入的 **operator 来做到这一点。由于 OP 明确要求 glob 模块,以下将返回一个行为相似的惰性求值迭代器

                import os, glob, itertools
                
                configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))
                

                请注意,在这种方法中,您只能在 configfiles 上迭代一次。如果您需要可在多个操作中使用的真实配置文件列表,则必须使用 list(configfiles) 显式创建此列表。

                【讨论】:

                  【解决方案11】:

                  这是一个改编版本,无需使用glob2 即可启用类似glob.glob 的功能。

                  def find_files(directory, pattern='*'):
                      if not os.path.exists(directory):
                          raise ValueError("Directory not found {}".format(directory))
                  
                      matches = []
                      for root, dirnames, filenames in os.walk(directory):
                          for filename in filenames:
                              full_path = os.path.join(root, filename)
                              if fnmatch.filter([full_path], pattern):
                                  matches.append(os.path.join(root, filename))
                      return matches
                  

                  所以如果你有以下目录结构

                  tests/files
                  ├── a0
                  │   ├── a0.txt
                  │   ├── a0.yaml
                  │   └── b0
                  │       ├── b0.yaml
                  │       └── b00.yaml
                  └── a1
                  

                  你可以这样做

                  files = utils.find_files('tests/files','**/b0/b*.yaml')
                  > ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']
                  

                  fnmatch 模式匹配整个文件名本身,而不仅仅是文件名。

                  【讨论】:

                    【解决方案12】:

                    glob2 包支持通配符并且相当快

                    code = '''
                    import glob2
                    glob2.glob("files/*/**")
                    '''
                    timeit.timeit(code, number=1)
                    

                    在我的笔记本电脑上,匹配 >60,000 file paths 大约需要 2 秒。

                    【讨论】:

                      【解决方案13】:

                      您可以在 Python 2.6 中使用 Formic

                      import formic
                      fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")
                      

                      披露 - 我是这个包的作者。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-06-04
                        • 2012-09-26
                        • 2019-07-27
                        • 2015-08-04
                        • 2019-11-24
                        相关资源
                        最近更新 更多