【问题标题】:How do I list all files of a directory?如何列出目录的所有文件?
【发布时间】:2011-03-13 13:00:25
【问题描述】:

如何在 Python 中列出一个目录的所有文件并将它们添加到 list

【问题讨论】:

标签: python directory


【解决方案1】:

os.listdir() 将为您提供目录中的所有内容 - 文件目录

如果你想要只是个文件,你可以使用os.path过滤掉它:

from os import listdir
from os.path import isfile, join
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]

或者您可以使用 os.walk(),它会为它访问的每个目录产生两个列表 - 分为 文件dirs 为您服务。如果您只想要顶级目录,则可以在第一次生成时中断

from os import walk

f = []
for (dirpath, dirnames, filenames) in walk(mypath):
    f.extend(filenames)
    break

或者,更短:

from os import walk

filenames = next(walk(mypath), (None, None, []))[2]  # [] if no file

【讨论】:

  • 简单一点:(_, _, filenames) = walk(mypath).next()(如果您确信 walk 将返回至少一个值,它应该返回。)
  • 稍作修改以存储完整路径:for (dirpath, dirnames, filenames) in os.walk(mypath): checksum_files.extend(os.path.join(dirpath, filename) for filename in filenames)休息
  • f.extend(filenames) 实际上并不等同于 f = f + filenamesextend 将就地修改f,而添加会在新的内存位置创建一个新列表。这意味着extend 通常比+ 更有效,但如果多个对象持有对列表的引用,有时会导致混淆。最后,值得注意的是f += filenames 等价于f.extend(filenames)not f = f + filenames
  • @misterbee,你的解决方案是最好的,只是一个小改进:_, _, filenames = next(walk(mypath), (None, None, []))
  • f += filenames 相当于扩展,而不是相反???天哪。
【解决方案2】:

我更喜欢使用glob 模块,因为它可以进行模式匹配和扩展。

import glob
print(glob.glob("/home/adam/*"))

它可以直观地进行模式匹配

import glob
# All files ending with .txt
print(glob.glob("/home/adam/*.txt")) 
# All files ending with .txt with depth of 2 folder
print(glob.glob("/home/adam/*/*.txt")) 

它将返回一个包含查询文件的列表:

['/home/adam/file1.txt', '/home/adam/file2.txt', .... ]

【讨论】:

  • 这是listdir+fnmatch的快捷方式docs.python.org/library/fnmatch.html#fnmatch.fnmatch
  • 澄清一下,这返回“完整路径”;它只是返回 glob 的扩展,无论它可能是什么。例如,给定/home/user/foo/bar/hello.txt,那么,如果在目录foo 中运行,glob("bar/*.txt") 将返回bar/hello.txt。在某些情况下,您实际上确实想要完整(即绝对)路径;对于这些情况,请参阅stackoverflow.com/questions/51520/…
  • 相关:使用 glob 递归查找文件:stackoverflow.com/a/2186565/4561887
  • 不回答这个问题。 glob.glob("*") 会。
  • 有没有办法确保从 glob 返回的项目是 only 文件?我询问文件 could 没有扩展名的情况(或其他文件和文件夹无法完全从路径字符串区分的情况)。我注意到这是一个适当的澄清,因为 OP 没有指定他们的文件是否有扩展名。
【解决方案3】:

当前目录下的列表

使用 os 模块中的 listdir 可以获取当前目录中的文件和文件夹

 import os

 arr = os.listdir()

查看目录

arr = os.listdir('c:\\files')

使用 glob,您可以指定要像这样列出的文件类型

import glob

txtfiles = []
for file in glob.glob("*.txt"):
    txtfiles.append(file)

mylist = [f for f in glob.glob("*.txt")]

获取当前目录中唯一文件的完整路径

import os
from os import listdir
from os.path import isfile, join

cwd = os.getcwd()
onlyfiles = [os.path.join(cwd, f) for f in os.listdir(cwd) if 
os.path.isfile(os.path.join(cwd, f))]
print(onlyfiles) 

['G:\\getfilesname\\getfilesname.py', 'G:\\getfilesname\\example.txt']

使用os.path.abspath获取完整路径名

你得到完整的路径作为回报

 import os
 files_path = [os.path.abspath(x) for x in os.listdir()]
 print(files_path)
 
 ['F:\\documenti\applications.txt', 'F:\\documenti\collections.txt']

Walk:遍历子目录

os.walk 返回根目录、目录列表和文件列表,这就是为什么我在 for 循环中将它们解压缩为 r、d、f 的原因;然后,它会在根目录的子文件夹中查找其他文件和目录,依此类推,直到没有子文件夹。

import os

# Getting the current work directory (cwd)
thisdir = os.getcwd()

# r=root, d=directories, f = files
for r, d, f in os.walk(thisdir):
    for file in f:
        if file.endswith(".docx"):
            print(os.path.join(r, file))

在目录树中向上移动

# Method 1
x = os.listdir('..')

# Method 2
x= os.listdir('/')

使用os.listdir()获取特定子目录的文件

import os

x = os.listdir("./content")

os.walk('.') - 当前目录

 import os
 arr = next(os.walk('.'))[2]
 print(arr)
 
 >>> ['5bs_Turismo1.pdf', '5bs_Turismo1.pptx', 'esperienza.txt']

next(os.walk('.')) 和 os.path.join('dir', 'file')

 import os
 arr = []
 for d,r,f in next(os.walk("F:\\_python")):
     for file in f:
         arr.append(os.path.join(r,file))

 for f in arr:
     print(files)

>>> F:\\_python\\dict_class.py
>>> F:\\_python\\programmi.txt

下一步……走

 [os.path.join(r,file) for r,d,f in next(os.walk("F:\\_python")) for file in f]
 
 >>> ['F:\\_python\\dict_class.py', 'F:\\_python\\programmi.txt']

os.walk

x = [os.path.join(r,file) for r,d,f in os.walk("F:\\_python") for file in f]
print(x)

>>> ['F:\\_python\\dict.py', 'F:\\_python\\progr.txt', 'F:\\_python\\readl.py']

os.listdir() - 只获取 txt 文件

 arr_txt = [x for x in os.listdir() if x.endswith(".txt")]
 

使用glob获取文件的完整路径

from path import path
from glob import glob

x = [path(f).abspath() for f in glob("F:\\*.txt")]

使用os.path.isfile 避开列表中的目录

import os.path
listOfFiles = [f for f in os.listdir() if os.path.isfile(f)]

在 Python 3.4 中使用 pathlib

import pathlib

flist = []
for p in pathlib.Path('.').iterdir():
    if p.is_file():
        print(p)
        flist.append(p)

list comprehension:

flist = [p for p in pathlib.Path('.').iterdir() if p.is_file()]

在 pathlib.Path() 中使用 glob 方法

import pathlib

py = pathlib.Path().glob("*.py")

使用 os.walk 获取所有且唯一的文件:仅检查返回的第三个元素,即文件列表

import os
x = [i[2] for i in os.walk('.')]
y=[]
for t in x:
    for f in t:
        y.append(f)

仅获取目录中带有next的文件:仅返回根文件夹中的文件

 import os
 x = next(os.walk('F://python'))[2]

只获取带有 next 的目录并进入一个目录,因为在 [1] 元素中只有文件夹

 import os
 next(os.walk('F://python'))[1] # for the current dir use ('.')
 
 >>> ['python3','others']

使用walk获取所有子目录名称

for r,d,f in os.walk("F:\\_python"):
    for dirs in d:
        print(dirs)

os.scandir() 来自 Python 3.5 及更高版本

import os
x = [f.name for f in os.scandir() if f.is_file()]

# Another example with scandir (a little variation from docs.python.org)
# This one is more efficient than os.listdir.
# In this case, it shows the files only in the current directory
# where the script is executed.

import os
with os.scandir() as i:
    for entry in i:
        if entry.is_file():
            print(entry.name)

【讨论】:

  • 这是对此处未提出的问题的太多答案的混搭。可能还值得解释一下注意事项或推荐的方法是什么。知道一种方法而不是 20 种方法来做同样的事情并不会更好,除非我也知道什么时候更适合使用。
  • 好的,我会尽快看一下我的答案,并尝试使其更干净,并提供有关方法等差异的更多有用信息。
  • 您不应该通过检查文件名是否包含子字符串来确定文件的扩展名。这可能会带来很多麻烦。我建议始终检查文件名是否以特定的子字符串结尾。
  • 这样的汇编可能会有所帮助,但这个答案尤其没有为现有答案增加任何价值。举个例子,[f for f in glob.glob("*.txt")] 等同于glob.glob("*.txt") 并且保证在这篇文章中没有额外的部分。它也非常罗嗦,并且有很多间距。可以通过添加解释或指出差异而不是列出另一个变体来进行改进。
  • 感谢您的评论,您当然是对的,我会尽快听从您的建议,使其更有用,也许这些年来我可以做出更好的答案。请给我一些时间来重新阐述它。
【解决方案4】:
import os
os.listdir("somedirectory")

将返回“somedirectory”中所有文件和目录的列表。

【讨论】:

  • 这将返回文件的相对路径,与glob.glob返回的完整路径相比
  • @JIXIang: os.listdir() 总是返回只是文件名(不是相对路径)。 glob.glob() 返回的内容是由输入模式的路径格式驱动的。
【解决方案5】:

获取仅文件列表(无子目录)的单行解决方案:

filenames = next(os.walk(path))[2]

或绝对路径名:

paths = [os.path.join(path, fn) for fn in next(os.walk(path))[2]]

【讨论】:

  • 如果您已经import os,请仅使用一条线。对我来说似乎不如 glob() 简洁。
  • glob 的问题是 glob('/home/adam/*.*') 会返回一个名为 'something.something' 的文件夹
  • 在 OS X 上,有一种叫做捆绑的东西。它是一个通常应被视为文件的目录(如 .tar)。您希望将那些视为文件还是目录?使用 glob() 会将其视为文件。您的方法会将其视为目录。
【解决方案6】:

从目录及其所有子目录中获取完整文件路径

import os

def get_filepaths(directory):
    """
    This function will generate the file names in a directory 
    tree by walking the tree either top-down or bottom-up. For each 
    directory in the tree rooted at directory top (including top itself), 
    it yields a 3-tuple (dirpath, dirnames, filenames).
    """
    file_paths = []  # List which will store all of the full filepaths.

    # Walk the tree.
    for root, directories, files in os.walk(directory):
        for filename in files:
            # Join the two strings in order to form the full filepath.
            filepath = os.path.join(root, filename)
            file_paths.append(filepath)  # Add it to the list.

    return file_paths  # Self-explanatory.

# Run the above function and store its results in a variable.   
full_file_paths = get_filepaths("/Users/johnny/Desktop/TEST")

  • 我在上述函数中提供的路径包含 3 个文件 - 其中两个位于根目录,另一个位于名为“SUBFOLDER”的子文件夹中。您现在可以执行以下操作:
  • print full_file_paths 将打印列表:

    • ['/Users/johnny/Desktop/TEST/file1.txt', '/Users/johnny/Desktop/TEST/file2.txt', '/Users/johnny/Desktop/TEST/SUBFOLDER/file3.dat']

如果您愿意,可以打开并阅读内容,或只关注扩展名为“.dat”的文件,如下面的代码所示:

for f in full_file_paths:
  if f.endswith(".dat"):
    print f

/Users/johnny/Desktop/TEST/SUBFOLDER/file3.dat

【讨论】:

    【解决方案7】:

    从 3.4 版开始,有内置的迭代器,它比os.listdir() 高效得多:

    pathlib3.4 版中的新功能。

    >>> import pathlib
    >>> [p for p in pathlib.Path('.').iterdir() if p.is_file()]
    

    根据PEP 428pathlib 库的目的是提供一个简单的类层次结构来处理文件系统路径和用户对其执行的常见操作。

    os.scandir(): 3.5 版中的新功能。

    >>> import os
    >>> [entry for entry in os.scandir('.') if entry.is_file()]
    

    注意os.walk()使用os.scandir()而不是3.5版本的os.listdir(),它的速度根据PEP 471提高了2-20倍。

    我还建议您阅读下面 ShadowRanger 的评论。

    【讨论】:

    • 谢谢!我认为这是唯一不直接返回list 的解决方案。如果愿意,也可以使用p.name 而不是第一个p
    • 欢迎您!我更喜欢生成pathlib.Path() 实例,因为它们有很多有用的方法,我不想浪费。您也可以在它们上调用str(p) 获取路径名称。
    • 注意:os.scandir 解决方案将比带有os.path.is_file 检查或类似的os.listdir 更有效,即使您需要list(所以您不会受益来自惰性迭代),因为 os.scandir 使用 OS 提供的 API,在迭代时免费为您提供 is_file 信息,根本没有每个文件往返磁盘到 stat 它们(在 Windows 上,@987654351 @s 为您免费提供完整的 stat 信息,在 *NIX 系统上,它需要 stat 以获取超出 is_fileis_dir 等的信息,但为方便起见,DirEntry 缓存在第一个 stat 上)。
    • 您也可以使用entry.name 仅获取文件名,或使用entry.path 获取其完整路径。没有更多的 os.path.join() 到处都是。
    【解决方案8】:

    前言

    • 尽管在问题文本中 filedirectory 术语之间有明显的区别,但有些人可能会认为目录实际上是特殊文件
    • 语句:“一个目录的所有文件”可以用两种方式解释:
      1. 所有直接(或1级)后代仅限
      2. 整个目录树中的所有后代(包括子目录中的后代)
    • 当被问到问题时,我认为 Python 2LTS 版本,但是代码示例将由 Python 3(.5)(我会尽可能让它们与 Python 2 兼容;此外,任何代码我要发布的属于 Python 的,来自 v3.5.4 - 除非另有说明)。这会产生与问题中另一个关键字相关的后果:“将它们添加到 列表”:

      • Python 2.2 之前的版本中,序列(可迭代对象)主要由列表(元组、集合等)表示
      • Python 2.2 中,引入了 generator ([Python.Wiki]: Generators)(由 [Python 3]: The yield statement 提供)的概念。随着时间的推移,生成器对应物开始出现在返回/使用列表的函数中
      • Python 3 中,生成器是默认行为
      • 不确定返回列表是否仍然是强制性的(或者生成器也可以),但是将生成器传递给 list 构造函数,会从中创建一个列表(并使用它) )。下面的示例说明了[Python 3]: map(function, iterable, ...) 上的差异
      >>> import sys
      >>> sys.version
      '2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]'
      >>> m = map(lambda x: x, [1, 2, 3])  # Just a dummy lambda function
      >>> m, type(m)
      ([1, 2, 3], <type 'list'>)
      >>> len(m)
      3
      


      >>> import sys
      >>> sys.version
      '3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]'
      >>> m = map(lambda x: x, [1, 2, 3])
      >>> m, type(m)
      (<map object at 0x000001B4257342B0>, <class 'map'>)
      >>> len(m)
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: object of type 'map' has no len()
      >>> lm0 = list(m)  # Build a list from the generator
      >>> lm0, type(lm0)
      ([1, 2, 3], <class 'list'>)
      >>>
      >>> lm1 = list(m)  # Build a list from the same generator
      >>> lm1, type(lm1)  # Empty list now - generator already consumed
      ([], <class 'list'>)
      
    • 示例将基于名为 root_dir 的目录,具有以下结构(此示例适用于 Win,但我在 上使用相同的树Lnx 也是):

      E:\Work\Dev\StackOverflow\q003207219>tree /f "root_dir"
      Folder PATH listing for volume Work
      Volume serial number is 00000029 3655:6FED
      E:\WORK\DEV\STACKOVERFLOW\Q003207219\ROOT_DIR
      ¦   file0
      ¦   file1
      ¦
      +---dir0
      ¦   +---dir00
      ¦   ¦   ¦   file000
      ¦   ¦   ¦
      ¦   ¦   +---dir000
      ¦   ¦           file0000
      ¦   ¦
      ¦   +---dir01
      ¦   ¦       file010
      ¦   ¦       file011
      ¦   ¦
      ¦   +---dir02
      ¦       +---dir020
      ¦           +---dir0200
      +---dir1
      ¦       file10
      ¦       file11
      ¦       file12
      ¦
      +---dir2
      ¦   ¦   file20
      ¦   ¦
      ¦   +---dir20
      ¦           file200
      ¦
      +---dir3
      


    解决方案

    程序化方法:

    1. [Python 3]: os.listdir(path='.')

      返回一个列表,其中包含路径给定的目录中条目的名称。该列表按任意顺序排列,不包括特殊条目'.''..' ...


      >>> import os
      >>> root_dir = "root_dir"  # Path relative to current dir (os.getcwd())
      >>>
      >>> os.listdir(root_dir)  # List all the items in root_dir
      ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
      >>>
      >>> [item for item in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, item))]  # Filter items and only keep files (strip out directories)
      ['file0', 'file1']
      

      更详细的示例(code_os_listdir.py):

      import os
      from pprint import pformat
      
      
      def _get_dir_content(path, include_folders, recursive):
          entries = os.listdir(path)
          for entry in entries:
              entry_with_path = os.path.join(path, entry)
              if os.path.isdir(entry_with_path):
                  if include_folders:
                      yield entry_with_path
                  if recursive:
                      for sub_entry in _get_dir_content(entry_with_path, include_folders, recursive):
                          yield sub_entry
              else:
                  yield entry_with_path
      
      
      def get_dir_content(path, include_folders=True, recursive=True, prepend_folder_name=True):
          path_len = len(path) + len(os.path.sep)
          for item in _get_dir_content(path, include_folders, recursive):
              yield item if prepend_folder_name else item[path_len:]
      
      
      def _get_dir_content_old(path, include_folders, recursive):
          entries = os.listdir(path)
          ret = list()
          for entry in entries:
              entry_with_path = os.path.join(path, entry)
              if os.path.isdir(entry_with_path):
                  if include_folders:
                      ret.append(entry_with_path)
                  if recursive:
                      ret.extend(_get_dir_content_old(entry_with_path, include_folders, recursive))
              else:
                  ret.append(entry_with_path)
          return ret
      
      
      def get_dir_content_old(path, include_folders=True, recursive=True, prepend_folder_name=True):
          path_len = len(path) + len(os.path.sep)
          return [item if prepend_folder_name else item[path_len:] for item in _get_dir_content_old(path, include_folders, recursive)]
      
      
      def main():
          root_dir = "root_dir"
          ret0 = get_dir_content(root_dir, include_folders=True, recursive=True, prepend_folder_name=True)
          lret0 = list(ret0)
          print(ret0, len(lret0), pformat(lret0))
          ret1 = get_dir_content_old(root_dir, include_folders=False, recursive=True, prepend_folder_name=False)
          print(len(ret1), pformat(ret1))
      
      
      if __name__ == "__main__":
          main()
      

      注意事项

      • 有两种实现方式:
        • 一个使用生成器的(当然在这里它似乎没用,因为我立即将结果转换为列表)
        • 经典的(函数名以_old结尾)
      • 使用递归(进入子目录)
      • 每个实现都有两个功能:
        • 下划线 (_)开头的一个:“private”(不应直接调用) - 完成所有工作
        • 公共路径(之前的包装器):它只是从返回的条目中剥离初始路径(如果需要)。这是一个丑陋的实现,但这是我目前唯一能想到的想法
      • 就性能而言,生成器通常要快一些(考虑到 creationiteration 次),但我没有在递归函数中测试它们,并且我也在内部生成器的函数内部迭代 - 不知道这对性能有多友好
      • 玩弄参数以获得不同的结果


      输出

      (py35x64_test) E:\Work\Dev\StackOverflow\q003207219>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" "code_os_listdir.py"
      <generator object get_dir_content at 0x000001BDDBB3DF10> 22 ['root_dir\\dir0',
       'root_dir\\dir0\\dir00',
       'root_dir\\dir0\\dir00\\dir000',
       'root_dir\\dir0\\dir00\\dir000\\file0000',
       'root_dir\\dir0\\dir00\\file000',
       'root_dir\\dir0\\dir01',
       'root_dir\\dir0\\dir01\\file010',
       'root_dir\\dir0\\dir01\\file011',
       'root_dir\\dir0\\dir02',
       'root_dir\\dir0\\dir02\\dir020',
       'root_dir\\dir0\\dir02\\dir020\\dir0200',
       'root_dir\\dir1',
       'root_dir\\dir1\\file10',
       'root_dir\\dir1\\file11',
       'root_dir\\dir1\\file12',
       'root_dir\\dir2',
       'root_dir\\dir2\\dir20',
       'root_dir\\dir2\\dir20\\file200',
       'root_dir\\dir2\\file20',
       'root_dir\\dir3',
       'root_dir\\file0',
       'root_dir\\file1']
      11 ['dir0\\dir00\\dir000\\file0000',
       'dir0\\dir00\\file000',
       'dir0\\dir01\\file010',
       'dir0\\dir01\\file011',
       'dir1\\file10',
       'dir1\\file11',
       'dir1\\file12',
       'dir2\\dir20\\file200',
       'dir2\\file20',
       'file0',
       'file1']
      


    1. [Python 3]: os.scandir(path='.')Python 3.5+,反向移植:[PyPI]: scandir

      返回os.DirEntry 对象的迭代器,该对象对应于path 给出的目录中的条目。条目按任意顺序生成,不包括特殊条目'.''..'

      使用scandir() 代替listdir() 可以显着提高还需要文件类型或文件属性信息的代码的性能,因为如果操作系统在扫描目录时提供了os.DirEntry 对象会公开此信息。所有os.DirEntry 方法都可以执行系统调用,但is_dir()is_file() 通常只需要对符号链接进行系统调用; os.DirEntry.stat() 在 Unix 上总是需要一个系统调用,但在 Windows 上只需要一个用于符号链接。


      >>> import os
      >>> root_dir = os.path.join(".", "root_dir")  # Explicitly prepending current directory
      >>> root_dir
      '.\\root_dir'
      >>>
      >>> scandir_iterator = os.scandir(root_dir)
      >>> scandir_iterator
      <nt.ScandirIterator object at 0x00000268CF4BC140>
      >>> [item.path for item in scandir_iterator]
      ['.\\root_dir\\dir0', '.\\root_dir\\dir1', '.\\root_dir\\dir2', '.\\root_dir\\dir3', '.\\root_dir\\file0', '.\\root_dir\\file1']
      >>>
      >>> [item.path for item in scandir_iterator]  # Will yield an empty list as it was consumed by previous iteration (automatically performed by the list comprehension)
      []
      >>>
      >>> scandir_iterator = os.scandir(root_dir)  # Reinitialize the generator
      >>> for item in scandir_iterator :
      ...     if os.path.isfile(item.path):
      ...             print(item.name)
      ...
      file0
      file1
      

      注意事项

      • 类似于os.listdir
      • 但它也更灵活(并提供更多功能)、更多 Pythonic(在某些情况下更快)


    1. [Python 3]: os.walk(top, topdown=True, onerror=None, followlinks=False)

      通过自上而下或自下而上遍历目录树来生成目录树中的文件名。对于以目录 top 为根的树中的每个目录(包括 top 本身),它会产生一个 3 元组(dirpathdirnamesfilenames)。


      >>> import os
      >>> root_dir = os.path.join(os.getcwd(), "root_dir")  # Specify the full path
      >>> root_dir
      'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir'
      >>>
      >>> walk_generator = os.walk(root_dir)
      >>> root_dir_entry = next(walk_generator)  # First entry corresponds to the root dir (passed as an argument)
      >>> root_dir_entry
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1'])
      >>>
      >>> root_dir_entry[1] + root_dir_entry[2]  # Display dirs and files (direct descendants) in a single list
      ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
      >>>
      >>> [os.path.join(root_dir_entry[0], item) for item in root_dir_entry[1] + root_dir_entry[2]]  # Display all the entries in the previous list by their full path
      ['E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file1']
      >>>
      >>> for entry in walk_generator:  # Display the rest of the elements (corresponding to every subdir)
      ...     print(entry)
      ...
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', ['dir00', 'dir01', 'dir02'], [])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00', ['dir000'], ['file000'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00\\dir000', [], ['file0000'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir01', [], ['file010', 'file011'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02', ['dir020'], [])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020', ['dir0200'], [])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020\\dir0200', [], [])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', [], ['file10', 'file11', 'file12'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', ['dir20'], ['file20'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2\\dir20', [], ['file200'])
      ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', [], [])
      

      注意事项

      • 在幕后,它使用os.scandir(旧版本为os.listdir
      • 它通过在子文件夹中重复来完成繁重的工作


    1. [Python 3]: glob.glob(pathname, *, recursive=False) ([Python 3]: glob.iglob(pathname, *, recursive=False))

      返回匹配路径名的路径名的可能为空的列表,它必须是包含路径规范的字符串。 pathname 可以是绝对路径(如 /usr/src/Python-1.5/Makefile)或相对路径(如 ../../Tools/*/*.gif),并且可以包含 shell 样式的通配符。结果中包含损坏的符号链接(如在 shell 中)。
      ...
      在 3.5 版中更改:支持使用“@987654377”的递归 glob @”。


      >>> import glob, os
      >>> wildcard_pattern = "*"
      >>> root_dir = os.path.join("root_dir", wildcard_pattern)  # Match every file/dir name
      >>> root_dir
      'root_dir\\*'
      >>>
      >>> glob_list = glob.glob(root_dir)
      >>> glob_list
      ['root_dir\\dir0', 'root_dir\\dir1', 'root_dir\\dir2', 'root_dir\\dir3', 'root_dir\\file0', 'root_dir\\file1']
      >>>
      >>> [item.replace("root_dir" + os.path.sep, "") for item in glob_list]  # Strip the dir name and the path separator from begining
      ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
      >>>
      >>> for entry in glob.iglob(root_dir + "*", recursive=True):
      ...     print(entry)
      ...
      root_dir\
      root_dir\dir0
      root_dir\dir0\dir00
      root_dir\dir0\dir00\dir000
      root_dir\dir0\dir00\dir000\file0000
      root_dir\dir0\dir00\file000
      root_dir\dir0\dir01
      root_dir\dir0\dir01\file010
      root_dir\dir0\dir01\file011
      root_dir\dir0\dir02
      root_dir\dir0\dir02\dir020
      root_dir\dir0\dir02\dir020\dir0200
      root_dir\dir1
      root_dir\dir1\file10
      root_dir\dir1\file11
      root_dir\dir1\file12
      root_dir\dir2
      root_dir\dir2\dir20
      root_dir\dir2\dir20\file200
      root_dir\dir2\file20
      root_dir\dir3
      root_dir\file0
      root_dir\file1
      

      注意事项

      • 使用os.listdir
      • 对于大树(特别是如果 recursive 开启),iglob 是首选
      • 允许基于名称的高级过滤(由于通配符)


    1. [Python 3]: class pathlib.Path(*pathsegments)Python 3.4+,反向移植:[PyPI]: pathlib2

      >>> import pathlib
      >>> root_dir = "root_dir"
      >>> root_dir_instance = pathlib.Path(root_dir)
      >>> root_dir_instance
      WindowsPath('root_dir')
      >>> root_dir_instance.name
      'root_dir'
      >>> root_dir_instance.is_dir()
      True
      >>>
      >>> [item.name for item in root_dir_instance.glob("*")]  # Wildcard searching for all direct descendants
      ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
      >>>
      >>> [os.path.join(item.parent.name, item.name) for item in root_dir_instance.glob("*") if not item.is_dir()]  # Display paths (including parent) for files only
      ['root_dir\\file0', 'root_dir\\file1']
      

      注意事项

      • 这是实现我们目标的一种方式
      • 这是处理路径的OOP风格
      • 提供许多功能


    1. [Python 2]: dircache.listdir(path)仅限 Python 2


      def listdir(path):
          """List directory contents, using cache."""
          try:
              cached_mtime, list = cache[path]
              del cache[path]
          except KeyError:
              cached_mtime, list = -1, []
          mtime = os.stat(path).st_mtime
          if mtime != cached_mtime:
              list = os.listdir(path)
              list.sort()
          cache[path] = mtime, list
          return list
      


    1. [man7]: OPENDIR(3) / [man7]: READDIR(3) / [man7]: CLOSEDIR(3) 通过[Python 3]: ctypes - A foreign function library for PythonPOSIX 特定)

      ctypes 是 Python 的外来函数库。它提供 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。它可用于将这些库包装在纯 Python 中。

      code_ctypes.py

      #!/usr/bin/env python3
      
      import sys
      from ctypes import Structure, \
          c_ulonglong, c_longlong, c_ushort, c_ubyte, c_char, c_int, \
          CDLL, POINTER, \
          create_string_buffer, get_errno, set_errno, cast
      
      
      DT_DIR = 4
      DT_REG = 8
      
      char256 = c_char * 256
      
      
      class LinuxDirent64(Structure):
          _fields_ = [
              ("d_ino", c_ulonglong),
              ("d_off", c_longlong),
              ("d_reclen", c_ushort),
              ("d_type", c_ubyte),
              ("d_name", char256),
          ]
      
      LinuxDirent64Ptr = POINTER(LinuxDirent64)
      
      libc_dll = this_process = CDLL(None, use_errno=True)
      # ALWAYS set argtypes and restype for functions, otherwise it's UB!!!
      opendir = libc_dll.opendir
      readdir = libc_dll.readdir
      closedir = libc_dll.closedir
      
      
      def get_dir_content(path):
          ret = [path, list(), list()]
          dir_stream = opendir(create_string_buffer(path.encode()))
          if (dir_stream == 0):
              print("opendir returned NULL (errno: {:d})".format(get_errno()))
              return ret
          set_errno(0)
          dirent_addr = readdir(dir_stream)
          while dirent_addr:
              dirent_ptr = cast(dirent_addr, LinuxDirent64Ptr)
              dirent = dirent_ptr.contents
              name = dirent.d_name.decode()
              if dirent.d_type & DT_DIR:
                  if name not in (".", ".."):
                      ret[1].append(name)
              elif dirent.d_type & DT_REG:
                  ret[2].append(name)
              dirent_addr = readdir(dir_stream)
          if get_errno():
              print("readdir returned NULL (errno: {:d})".format(get_errno()))
          closedir(dir_stream)
          return ret
      
      
      def main():
          print("{:s} on {:s}\n".format(sys.version, sys.platform))
          root_dir = "root_dir"
          entries = get_dir_content(root_dir)
          print(entries)
      
      
      if __name__ == "__main__":
          main()
      

      注意事项

      • 它从 libc 加载三个函数(在当前进程中加载​​)并调用它们(更多详细信息请查看[SO]: How do I check whether a file exists without exceptions? (@CristiFati's answer) - 来自项目 #4 的最后注释。)。这将使这种方法非常接近 Python / C 边缘
      • LinuxDirent64 是我机器中 [man7]: dirent.h(0P)struct dirent64ctypes 表示(DT_ 常量也是如此) :Ubtu 16 x644.10.0-40-genericlibc6-dev:amd64)。在其他风格/版本上,结构定义可能不同,如果是这样,则应更新 ctypes 别名,否则将产生 Undefined Behavior
      • 它以os.walk 的格式返回数据。我没有费心让它递归,但从现有代码开始,这将是一项相当微不足道的任务
      • Win 上一切都可以实现,但数据(库、函数、结构、常量等)不同


      输出

      [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q003207219]> ./code_ctypes.py
      3.5.2 (default, Nov 12 2018, 13:43:14)
      [GCC 5.4.0 20160609] on linux
      
      ['root_dir', ['dir2', 'dir1', 'dir3', 'dir0'], ['file1', 'file0']]
      


    1. [ActiveState.Docs]: win32file.FindFilesWWin 特定)

      使用 Windows Unicode API 检索匹配文件名的列表。 API FindFirstFileW/FindNextFileW/Find 关闭函数的接口。


      >>> import os, win32file, win32con
      >>> root_dir = "root_dir"
      >>> wildcard = "*"
      >>> root_dir_wildcard = os.path.join(root_dir, wildcard)
      >>> entry_list = win32file.FindFilesW(root_dir_wildcard)
      >>> len(entry_list)  # Don't display the whole content as it's too long
      8
      >>> [entry[-2] for entry in entry_list]  # Only display the entry names
      ['.', '..', 'dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1']
      >>>
      >>> [entry[-2] for entry in entry_list if entry[0] & win32con.FILE_ATTRIBUTE_DIRECTORY and entry[-2] not in (".", "..")]  # Filter entries and only display dir names (except self and parent)
      ['dir0', 'dir1', 'dir2', 'dir3']
      >>>
      >>> [os.path.join(root_dir, entry[-2]) for entry in entry_list if entry[0] & (win32con.FILE_ATTRIBUTE_NORMAL | win32con.FILE_ATTRIBUTE_ARCHIVE)]  # Only display file "full" names
      ['root_dir\\file0', 'root_dir\\file1']
      

      注意事项


    1. 安装一些(其他)第三方包来解决问题
      • 很可能会依赖上述一项(或多项)(可能需要进行轻微的自定义)


    注意事项

    • 代码应该是可移植的(针对特定区域的地方除外 - 已标记)或交叉:

      • 平台(NixWin、)
      • Python 版本(2、3、)
    • 在上述变体中使用了多种路径样式(绝对、相对),以说明所使用的“工具”在这个方向上是灵活的

    • os.listdiros.scandir 使用 opendir / readdir / closedir ([MS.Docs]: FindFirstFileW function / [MS.Docs]: FindNextFileW function / @ 987654354@)(通过[GitHub]: python/cpython - (master) cpython/Modules/posixmodule.c

    • win32file.FindFilesW 也使用那些(Win 特定的)功能(通过[GitHub]: mhammond/pywin32 - (master) pywin32/win32/src/win32file.i

    • _get_dir_content(从 #1. 点开始)可以使用这些方法中的任何一种来实现(有些需要更多的工作,有些需要更少的工作)

      • 可以进行一些高级过滤(而不仅仅是文件 vs. 目录):例如include_folders 参数可以替换为另一个参数(例如 filter_func),这将是一个将路径作为参数的函数:filter_func=lambda x: True(这不会去掉任何东西)和在 _get_dir_content 内部类似:if not filter_func(entry_with_path): continue(如果函数针对一个条目失败,将被跳过),但代码变得越复杂,执行时间就越长李>
    • Nota bene!由于使用了递归,我必须提到我在我的笔记本电脑(Win 10 x64)上做了一些测试,与这个问题完全无关,当递归级别达到 (990 .. 1000) 范围内的某个值时(recursionlimit - 1000(默认)),我得到了 StackOverflow时间> :)。如果目录树超过了这个限制(我不是 FS 专家,所以我不知道这是否可能),这可能是个问题。
      我还必须提到,我没有尝试增加 recursionlimit,因为我在该领域没有经验(在必须增加 OS的堆栈之前,我可以增加多少> 级别),但理论上总是有失败的可能性,如果 dir 深度大于可能的最高 recursionlimit(在该机器上)

    • 代码示例仅用于演示目的。这意味着我没有考虑错误处理(我认为没有任何 try / 除了 / else / finally 块),所以代码不健壮(原因是:保留它尽可能简单和简短)。对于生产,还应添加错误处理

    其他方法:

    1. 仅将 Python 用作包装器

      • 一切都使用另一种技术完成
      • 该技术是从 Python 调用的
      • 我所知道的最著名的风格是我所说的系统管理员方法:

        • 使用 Python(或任何编程语言)来执行 shell 命令(并解析它们的输出)
        • 有些人认为这是一个巧妙的技巧
        • 我认为它更像是一个蹩脚的解决方法 (gainarie),因为操作本身是从 shell 执行的(在这种情况下为 cmd ),因此与 Python 没有任何关系。
        • 过滤(grep/findstr)或输出格式可以在双方进行,但我不会坚持。另外,我特意用os.system代替subprocess.Popen
        (py35x64_test) E:\Work\Dev\StackOverflow\q003207219>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os;os.system(\"dir /b root_dir\")"
        dir0
        dir1
        dir2
        dir3
        file0
        file1
        

      一般要避免这种方法,因为如果某些命令输出格式在 OS 版本/风格之间略有不同,则解析代码也应进行调整;更不用说语言环境之间的差异了)。

    【讨论】:

      【解决方案9】:

      我很喜欢adamk's answer,建议你使用glob(),来自同名模块。这允许您与*s 进行模式匹配。

      但正如其他人在 cmets 中指出的那样,glob() 可能会因斜线方向不一致而绊倒。为了解决这个问题,我建议您使用 os.path 模块中的 join()expanduser() 函数,也可以使用 os 模块中的 getcwd() 函数。

      举例:

      from glob import glob
      
      # Return everything under C:\Users\admin that contains a folder called wlp.
      glob('C:\Users\admin\*\wlp')
      

      上述情况很糟糕 - 路径已被硬编码,并且只能在驱动器名称和被硬编码到路径中的 \s 之间的 Windows 上工作。

      from glob    import glob
      from os.path import join
      
      # Return everything under Users, admin, that contains a folder called wlp.
      glob(join('Users', 'admin', '*', 'wlp'))
      

      上面的方法效果更好,但它依赖于文件夹名称Users,该名称在 Windows 上很常见,而在其他操作系统上并不常见。它还依赖于具有特定名称的用户admin

      from glob    import glob
      from os.path import expanduser, join
      
      # Return everything under the user directory that contains a folder called wlp.
      glob(join(expanduser('~'), '*', 'wlp'))
      

      这适用于所有平台。

      另一个很好的例子,可以完美地跨平台运行并且做一些不同的事情:

      from glob    import glob
      from os      import getcwd
      from os.path import join
      
      # Return everything under the current directory that contains a folder called wlp.
      glob(join(getcwd(), '*', 'wlp'))
      

      希望这些示例可以帮助您了解在标准 Python 库模块中可以找到的一些函数的强大功能。

      【讨论】:

      【解决方案10】:
      def list_files(path):
          # returns a list of names (with extension, without full path) of all files 
          # in folder path
          files = []
          for name in os.listdir(path):
              if os.path.isfile(os.path.join(path, name)):
                  files.append(name)
          return files 
      

      【讨论】:

        【解决方案11】:

        如果您正在寻找 find 的 Python 实现,这是我经常使用的方法:

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

        所以我用它制作了一个 PyPI package,还有一个 GitHub repository。我希望有人发现它可能对这段代码有用。

        【讨论】:

          【解决方案12】:

          为了获得更好的结果,您可以将os 模块的listdir() 方法与生成器一起使用(生成器是保持其状态的强大迭代器,还记得吗?)。以下代码适用于两个版本:Python 2 和 Python 3。

          这是一个代码:

          import os
          
          def files(path):  
              for file in os.listdir(path):
                  if os.path.isfile(os.path.join(path, file)):
                      yield file
          
          for file in files("."):  
              print (file)
          

          listdir() 方法返回给定目录的条目列表。如果给定条目是文件,则方法 os.path.isfile() 返回 Trueyield 运算符退出函数但保持其当前状态,并且它仅返回检测为文件的条目的名称。以上所有内容都允许我们循环生成器函数。

          【讨论】:

            【解决方案13】:

            返回绝对文件路径列表,不会递归到子目录中

            L = [os.path.join(os.getcwd(),f) for f in os.listdir('.') if os.path.isfile(os.path.join(os.getcwd(),f))]
            

            【讨论】:

            • 注意:os.path.abspath(f) 会比os.path.join(os.getcwd(),f) 便宜一些。
            • 如果你从 cwd = os.path.abspath('.') 开始,然后使用 cwd 而不是 '.'os.getcwd() 以避免负载冗余系统调用,我会更有效率。
            【解决方案14】:

            一位睿智的老师曾经告诉我:

            当有几种既定的方法来做某事时,它们都不适合所有情况。

            因此,我将为问题的子集添加一个解决方案:很多时候,我们只想检查文件是否匹配开始字符串和结束字符串,而不需要进入子目录。因此,我们想要一个返回文件名列表的函数,例如:

            filenames = dir_filter('foo/baz', radical='radical', extension='.txt')
            

            如果您想先声明两个函数,可以这样做:

            def file_filter(filename, radical='', extension=''):
                "Check if a filename matches a radical and extension"
                if not filename:
                    return False
                filename = filename.strip()
                return(filename.startswith(radical) and filename.endswith(extension))
            
            def dir_filter(dirname='', radical='', extension=''):
                "Filter filenames in directory according to radical and extension"
                if not dirname:
                    dirname = '.'
                return [filename for filename in os.listdir(dirname)
                            if file_filter(filename, radical, extension)]
            

            这个解决方案可以很容易地用正则表达式来概括(如果你不希望你的模式总是停留在文件名的开头或结尾,你可能想要添加一个pattern 参数)。

            【讨论】:

              【解决方案15】:
              import os
              import os.path
              
              
              def get_files(target_dir):
                  item_list = os.listdir(target_dir)
              
                  file_list = list()
                  for item in item_list:
                      item_dir = os.path.join(target_dir,item)
                      if os.path.isdir(item_dir):
                          file_list += get_files(item_dir)
                      else:
                          file_list.append(item_dir)
                  return file_list
              

              这里我使用递归结构。

              【讨论】:

              • 同样可以用pathlib一行来实现:filter(Path.is_file, Path().rglob('*'))
              【解决方案16】:

              使用生成器

              import os
              def get_files(search_path):
                   for (dirpath, _, filenames) in os.walk(search_path):
                       for filename in filenames:
                           yield os.path.join(dirpath, filename)
              list_files = get_files('.')
              for filename in list_files:
                  print(filename)
              

              【讨论】:

              • 谢谢!正是我需要的。顶部的很多答案都已过时,不适用于 Python 3.9 :)
              【解决方案17】:

              Python 3.4+ 的另一个非常易读的变体是使用 pathlib.Path.glob:

              from pathlib import Path
              folder = '/foo'
              [f for f in Path(folder).glob('*') if f.is_file()]
              

              更具体很简单,例如仅在所有子目录中查找不是符号链接的 Python 源文件:

              [f for f in Path(folder).glob('**/*.py') if not f.is_symlink()]
              

              【讨论】:

                【解决方案18】:

                对于 Python 2:

                pip install rglob
                

                那就做吧

                import rglob
                file_list = rglob.rglob("/home/base/dir/", "*")
                print file_list
                

                【讨论】:

                • 如果可以避免外部部署,那就去做。当您所需要的一切都已经在语言中时,使用外部依赖项的附加价值是什么?
                【解决方案19】:

                这是我的通用函数。它返回文件路径列表而不是文件名,因为我发现它更有用。它有一些可选参数,使其具有通用性。例如,我经常将它与pattern='*.txt'subfolders=True 之类的参数一起使用。

                import os
                import fnmatch
                
                def list_paths(folder='.', pattern='*', case_sensitive=False, subfolders=False):
                    """Return a list of the file paths matching the pattern in the specified 
                    folder, optionally including files inside subfolders.
                    """
                    match = fnmatch.fnmatchcase if case_sensitive else fnmatch.fnmatch
                    walked = os.walk(folder) if subfolders else [next(os.walk(folder))]
                    return [os.path.join(root, f)
                            for root, dirnames, filenames in walked
                            for f in filenames if match(f, pattern)]
                

                【讨论】:

                  【解决方案20】:

                  我将提供一个示例,其中可以提供源路径和文件类型作为输入。该代码返回带有 csv 扩展名的文件名列表。如果需要返回所有文件,请使用 .。这也将递归地扫描子目录。

                  [y for x in os.walk(sourcePath) for y in glob(os.path.join(x[0], '*.csv'))]

                  根据需要修改文件扩展名和源路径。

                  【讨论】:

                  • 如果你打算使用glob,那么就使用glob('**/*.csv', recursive=True)。无需将其与 os.walk() 结合起来进行递归(自 Python 3.5 起支持 recursive**)。
                  【解决方案21】:

                  dircache 是“自 2.6 版起已弃用:dircache 模块已在 Python 3.0 中删除。”

                  import dircache
                  list = dircache.listdir(pathname)
                  i = 0
                  check = len(list[0])
                  temp = []
                  count = len(list)
                  while count != 0:
                    if len(list[i]) != check:
                       temp.append(list[i-1])
                       check = len(list[i])
                    else:
                      i = i + 1
                      count = count - 1
                  
                  print temp
                  

                  【讨论】:

                    猜你喜欢
                    • 2014-03-08
                    • 2017-02-16
                    • 2011-06-13
                    • 2010-10-04
                    • 2011-08-05
                    • 2018-11-10
                    • 1970-01-01
                    相关资源
                    最近更新 更多