如何列出目录中的所有文件?

3466

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


27
如何获取子目录列表相关。 - rds
21个回答

6323

os.listdir() 返回目录中的所有内容,包括文件目录

os.pathisfile()可以用来仅列出文件:

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

或者,os.walk() 对于它访问的每个目录,会返回两个列表 -- 一个是文件列表,另一个是目录列表。如果你只想要顶级目录,你可以在第一次返回时中断。
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

16
为了递归地获取所有子文件夹中文件的完整路径: [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(mypath) for f in filenames] - Nir
为什么需要使用break? - João Víctor Melo
3
@João Víctor Melo os.walk() 递归访问所有子目录,它们的子目录等等。通过中断,我们只访问第一个目录 mypath - OrOrg
老兄,这些一行代码简直太疯狂了。 - prototorpedo

2529

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

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

它可以直观地进行模式匹配。
import glob
# All files and directories ending with .txt and that don't begin with a dot:
print(glob.glob("/home/adam/*.txt")) 
# All files and directories ending with .txt with depth of 2 folders, ignoring names beginning with a dot:
print(glob.glob("/home/adam/*/*.txt")) 

它将返回一个包含查询文件和目录的列表:
['/home/adam/file1.txt', '/home/adam/file2.txt', .... ]

请注意,glob忽略以点.开头的文件和目录,因为它们被视为隐藏文件和目录,除非模式类似于.*
使用glob.escape来转义不是用作模式的字符串:
print(glob.glob(glob.escape(directory_name) + "/*.txt"))

2
from glob import globglob() 替换了 glob.glob() - young_souvlaki
7
from glob import glob as gglob() 替换为 g() - Cedric H.

1552

列出当前目录中的文件列表

使用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']

遍历:进入子目录

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)

使用列表推导式:
```python ```
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和walk在目录中获取目录,因为在[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)

Python 3.5及以上版本中的os.scandir()

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)

989
import os
os.listdir("somedirectory")

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


为什么这不是第一名呢?这种方法有什么缺点吗?看起来这是最简单的方法。 - undefined

192

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

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

或绝对路径名:

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

161

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

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”的子文件夹中。你现在可以执行以下操作:
  • 打印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


115
自3.4版本起,内置了用于此目的的迭代器,比os.listdir()更高效: pathlib:自3.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()]

请注意,从3.5版本开始,os.walk()使用os.scandir()而不是os.listdir(),根据PEP 471,其速度提高了2-20倍。另外,我建议阅读ShadowRanger在下面的评论。

10
注意:使用os.scandir方法会比较高效,相对于使用类似 os.listdir 加上 os.path.is_file检查等方式,哪怕你需要一个列表(所以你无法从懒惰迭代中受益),因为os.scandir使用操作系统提供的API来遍历目录,并且在迭代过程中可以免费获得is_file信息,完全不需要针对每个文件进行磁盘stat调用(在Windows上,DirEntry会免费提供完整的stat信息,而在* NIX系统上,需要对超出is_fileis_dir等信息的信息进行stat调用,但是DirEntry会缓存第一次stat以方便后续调用)。 - ShadowRanger

75

初步说明

  • Although there's a clear differentiation between file and directory terms in the question text, some may argue that directories are actually special files

  • The statement: "all files of a directory" can be interpreted in two ways:

    1. All direct (or level 1) descendants only

    2. All descendants in the whole directory tree (including the ones in sub-directories)

  • When the question was asked, I imagine that Python 2, was the LTS version, however the code samples will be run by Python 3(.5) (I'll keep them as Python 2 compliant as possible; also, any code belonging to Python that I'm going to post, is from v3.5.4 - unless otherwise specified).
    That has consequences related to another keyword in the question: "add them into a list":

    >>> 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 exhausted
    ([], <class 'list'>)
    
  • The examples will be based on a directory called root_dir with the following structure (this example is for Win, but I'm using the same tree on Nix as well). Note that I'll be reusing the console:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q003207219]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> 
    [prompt]> 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.Docs]: os.listdir(path='.')

返回一个列表,其中包含给定路径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):

#!/usr/bin/env python

import os
import sys
from pprint import pformat as pf


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(*argv):
    root_dir = "root_dir"
    ret0 = get_dir_content(root_dir, include_folders=True, recursive=True, prepend_folder_name=True)
    lret0 = list(ret0)
    print("{:} {:d}\n{:s}".format(ret0, len(lret0), pf(lret0)))
    ret1 = get_dir_content_old(root_dir, include_folders=False, recursive=True, prepend_folder_name=False)
    print("\n{:d}\n{:s}".format(len(ret1), pf(ret1)))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

注释:

  • 有两种实现方式:

    1. 一种使用生成器(当然,在这里似乎没有用处,因为我立即将结果转换为列表)

    2. 经典的实现方式(函数名称以_old结尾)

  • 使用递归(进入子目录)

  • 对于每种实现方式,都有两个函数:

    • 一个以下划线(_)开头的“私有”函数(不应直接调用)- 它执行所有工作

    • 公共函数(上一个函数的包装器):它只是从返回的条目中删除初始路径(如果需要)。这是一种丑陋的实现方式,但这是我目前能想到的唯一想法

  • 就性能而言,生成器通常会稍微快一点(考虑到创建迭代时间),但我没有在递归函数中测试它们,而且我正在函数内部遍历内部生成器 - 不知道这是否友好的性能

  • 通过调整参数来获得不同的结果

输出:

[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.05.04_test0\Scripts\python.exe" ".\code_os_listdir.py"
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] 064bit on win32

<generator object get_dir_content at 0x000002C080418F68> 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']

Done.

2. [Python.Docs]: os.scandir(path='.')

仅适用于 Python 3.5+,可回溯使用:[PyPI]: scandir

返回一个迭代器,其中包含与给定路径中的条目对应的 os.DirEntry 对象。 条目以任意顺序生成,并且不包括特殊条目 '.''..'

使用 scandir() 而不是 listdir() 可以显著提高需要文件类型或文件属性信息的代码的性能,因为如果操作系统在扫描目录时提供此信息,则 os.DirEntry 对象会公开此信息。 所有 os.DirEntry 方法都可能执行系统调用,但是 is_dir()is_file() 通常仅对符号链接需要系统调用; 在 Unix 上,os.DirEntry.stat() 总是需要系统调用,但在 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 类似

  • 但它更加灵活(并提供更多功能),更符合 Python 风格(在某些情况下,更快)

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

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

>>> 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(在旧版本(Python)中使用os.listdir)。

  • 它通过递归子文件夹来进行重要的操作。

4. [Python.Docs]: glob.glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False)

或者glob.iglob

返回与pathname匹配的路径名列表(可能为空)。pathname必须是包含路径规范的字符串。路径可以是绝对路径(如 /usr/src/Python-1.5/Makefile)或相对路径(如../../Tools/*/*.gif),并且可以包含类似shell的通配符。结果包括破损的符号链接(就像在shell中一样)。
...
从版本3.5开始更改:支持使用“**”进行递归的全局搜索。


>>> 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

  • 允许基于名称进行高级过滤(由于通配符)

5. [Python.Docs]: 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风格

  • 提供了许多功能

6. [Python 2.Docs]: dircache.listdir(path)


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

7. 本地操作系统 API

POSIX 特定:

通过[Python.Docs]: ctypes - Python 的外部函数库可用:

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

虽然不是直接相关的,但在使用 CTypes 之前,请查看[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)

code_ctypes.py:

#!/usr/bin/env python3

import ctypes as cts
import sys


DT_DIR = 4
DT_REG = 8


class NixDirent64(cts.Structure):
    _fields_ = (
        ("d_ino", cts.c_ulonglong),
        ("d_off", cts.c_longlong),
        ("d_reclen", cts.c_ushort),
        ("d_type", cts.c_ubyte),
        ("d_name", cts.c_char * 256),
    )

NixDirent64Ptr = cts.POINTER(NixDirent64)


libc = this_process = cts.CDLL(None, use_errno=True)

opendir = libc.opendir
opendir.argtypes = (cts.c_char_p,)
opendir.restype = cts.c_void_p
readdir = libc.readdir
readdir.argtypes = (cts.c_void_p,)
readdir.restype = NixDirent64Ptr
closedir = libc.closedir
closedir.argtypes = (cts.c_void_p,)


def get_dir_content(path):
    ret = [path, [], []]
    pdir = opendir(cts.create_string_buffer(path.encode()))
    if not pdir:
        print("opendir returned NULL (errno: {:d})".format(cts.get_errno()))
        return ret
    cts.set_errno(0)
    while True:
        pdirent = readdir(pdir)
        if not pdirent:
            break
        dirent = pdirent.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)
    if cts.get_errno():
        print("readdir returned NULL (errno: {:d})".format(cts.get_errno()))
    closedir(pdir)
    return ret


def main(*argv):
    root_dir = "root_dir"
    entries = get_dir_content(root_dir)
    print("Entries:\n{:}".format(entries))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

注释:

  • 它从LibC (libc.so - 加载在当前进程中) 中加载三个函数并调用它们 (更多细节请查看[SO]: How do I check whether a file exists without exceptions? (@CristiFati's answer) - 最后注意事项来自项目 #4.). 这使得这种方法非常接近于Python / C 的边缘。

  • NixDirent64Ubuntu OS[Man7]: dirent.h(0P)struct dirent64CTypes表示形式 (所以也是DT_常量)。在其他版本/版本上,结构定义可能不同,如果是这样,CTypes别名应该更新,否则它将产生未定义行为

  • 它以os.walk的格式返回数据。我没有费心去做递归,但从现有代码开始,这将是一个相当简单的任务。

  • Win上也可以完成所有操作,但数据(库、函数、结构体、常量等)有所不同。

输出:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q003207219]> python3.5 ./code_ctypes.py
Python 3.5.10 (default, Jan 15 2022, 19:53:00) [GCC 9.3.0] 064bit on linux

Entries:
['root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1']]

Done.

8. [TimGolden]: win32file.FindFilesW

适用于Windows系统:

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


>>> import os, win32file as wfile, win32con as wcon
>>>
>>> root_dir = "root_dir"
>>> root_dir_wildcard = os.path.join(root_dir, "*")
>>> entry_list = wfile.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] & wcon.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] & (wcon.FILE_ATTRIBUTE_NORMAL | wcon.FILE_ATTRIBUTE_ARCHIVE)]  # Only display file "full" names
['root_dir\\file0', 'root_dir\\file1']

注意:

9. 使用其他第三方包来解决问题

很可能会依赖上述其中一个或多个(可能会进行细微的自定义)。

注意:

  • 代码应该是可移植的(除了针对特定区域的地方-这些地方已经标记出来)或跨平台:

    • 操作系统NixWin,)

    • Python版本(2,3,)

  • 上述变体使用了多种路径样式(绝对、相对),以说明所使用的“工具”在此方向上是灵活的。

  • os.listdiros.scandir使用opendir/readdir/closedir(通过[GitHub]:python/cpython - (main) cpython/Modules/posixmodule.c)(通过[MS.Learn]: FindFirstFileW function (fileapi.h) / [MS.Learn]: FindNextFileW function (fileapi.h) / [MS.Learn]: FindClose function (fileapi.h)

  • win32file.FindFilesW也使用这些(Win特定的)函数(通过[GitHub]:mhammond/pywin32 - (main) 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 pc064)上进行了一些与此问题无关的测试,当递归级别达到(990 .. 1000)范围(recursionlimit-1000(默认值))时,我得到了StackOverflow:)。如果目录树超过该限制(我不是FS专家,因此不知道是否可能),那可能是个问题。
    我还必须提到,我没有尝试增加recursionlimit,但理论上,如果dir深度大于该机器上最高可能的recursionlimit,则总会存在失败的可能性。
    有关此主题的更多详细信息,请参见[Python.Docs]:subprocess - 子进程管理例程(runcheck_output,...)

[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.05.04_test0\Scripts\python.exe" -c "import os;os.system(\"dir /b root_dir\")"
dir0
dir1
dir2
dir3
file0
file1

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q003207219]> python3.5 -c "import os;os.system(\"ls root_dir\")"
dir0  dir1  dir2  dir3  file0  file1

一般来说,应该避免使用这种方法,因为如果某些命令的输出格式在不同的操作系统版本/类型之间略有不同,解析代码也应相应适应 - 更不用说区域设置之间的差异了。


50

我非常喜欢adamk的答案,他建议您使用名为glob()的模块,这允许您使用*进行模式匹配。

但正如其他人在评论中指出的那样,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')

上述代码很糟糕 - 文件路径已经硬编码,只能在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库模块中可用的某些函数的强大功能。

4
额外的 glob 函数:从 Python 3.5 开始,只要将 recursive=True** 就可以使用。参见此处文档:https://docs.python.org/3.5/library/glob.html#glob.glob - ArtOfWarfare

40
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 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接