如何在Python中列出目录中的所有文件,并将它们添加到一个list
中?
os.listdir()
返回目录中的所有内容,包括文件和目录。
os.path
的isfile()
可以用来仅列出文件:
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
[os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(mypath) for f in filenames]
- Niros.walk()
递归访问所有子目录,它们的子目录等等。通过中断,我们只访问第一个目录 mypath
。 - OrOrg我更喜欢使用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"))
from glob import glob
用 glob()
替换了 glob.glob()
。 - young_souvlakifrom glob import glob as g
将 glob()
替换为 g()
。 - Cedric H.列出当前目录中的文件列表
使用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)]
pathlib
模块import pathlib
flist = []
for p in pathlib.Path('.').iterdir():
if p.is_file():
print(p)
flist.append(p)
flist = [p for p in pathlib.Path('.').iterdir() if p.is_file()]
import pathlib
py = pathlib.Path().glob("*.py")
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)
import os
os.listdir("somedirectory")
将返回“somedirectory”中所有文件和目录的列表。
获取仅文件列表(无子目录)的一行解决方案:
filenames = next(os.walk(path))[2]
或绝对路径名:
paths = [os.path.join(path, fn) for fn in next(os.walk(path))[2]]
从目录及其所有子目录中获取完整文件路径
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")
打印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
os.listdir()
更高效:
pathlib
:自3.4版本以来新增。>>> import pathlib
>>> [p for p in pathlib.Path('.').iterdir() if p.is_file()]
pathlib
库的目的是提供一个简单的类层次结构来处理文件系统路径和用户对它们执行的常见操作。
os.scandir()
:自版本 3.5 起新增。>>> import os
>>> [entry for entry in os.scandir('.') if entry.is_file()]
os.walk()
使用os.scandir()
而不是os.listdir()
,根据PEP 471,其速度提高了2-20倍。另外,我建议阅读ShadowRanger在下面的评论。os.scandir
方法会比较高效,相对于使用类似 os.listdir
加上 os.path.is_file
检查等方式,哪怕你需要一个列表(所以你无法从懒惰迭代中受益),因为os.scandir
使用操作系统提供的API来遍历目录,并且在迭代过程中可以免费获得is_file
信息,完全不需要针对每个文件进行磁盘stat
调用(在Windows上,DirEntry
会免费提供完整的stat
信息,而在* NIX系统上,需要对超出is_file
,is_dir
等信息的信息进行stat
调用,但是DirEntry
会缓存第一次stat
以方便后续调用)。 - ShadowRangerAlthough 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:
All direct (or level 1) descendants only
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":
In pre Python 2.2 versions, sequences (iterables) were mostly represented by lists (tuples, sets, ...)
In Python 2.2, the concept of generator ([Python.Wiki]: Generators) - courtesy of [Python.Docs]: Simple statements - The yield statement) - was introduced. As time passed, generator counterparts started to appear for functions that returned / worked with lists
In Python 3, generator is the default behavior
Not sure if returning a list is still mandatory (or a generator would do as well), but passing a generator to the list constructor, will create a list out of it (and also consume it). The example below illustrates the differences on [Python.Docs]: Built-in functions - map(function, iterable, *iterables)
>>> 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
返回一个列表,其中包含给定路径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)
注释:
有两种实现方式:
一种使用生成器(当然,在这里似乎没有用处,因为我立即将结果转换为列表)
经典的实现方式(函数名称以_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.
仅适用于 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 风格(在某些情况下,更快)
通过自上而下或自下而上地遍历树形目录结构来生成目录树中的文件名。对于以目录 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)。
它通过递归子文件夹来进行重要的操作。
或者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
允许基于名称进行高级过滤(由于通配符)
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风格
提供了许多功能
仅适用于Python 2
但是,根据[GitHub]: python/cpython - (2.7) cpython/Lib/dircache.py所述,它只是一个带有缓存的os.listdir的(薄)包装器
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
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 的边缘。
NixDirent64是Ubuntu OS中[Man7]: dirent.h(0P)的struct dirent64的CTypes表示形式 (所以也是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.
适用于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']
注意:
很可能会依赖上述其中一个或多个(可能会进行细微的自定义)。
代码应该是可移植的(除了针对特定区域的地方-这些地方已经标记出来)或跨平台:
操作系统(Nix,Win,)
Python版本(2,3,)
上述变体使用了多种路径样式(绝对、相对),以说明所使用的“工具”在此方向上是灵活的。
os.listdir和os.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。)可以使用这些方法之一来实现(有些需要更多的工作,有些则需要更少的工作)
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 - 子进程管理例程(run,check_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
一般来说,应该避免使用这种方法,因为如果某些命令的输出格式在不同的操作系统版本/类型之间略有不同,解析代码也应相应适应 - 更不用说区域设置之间的差异了。
我非常喜欢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库模块中可用的某些函数的强大功能。recursive=True
,**
就可以使用。参见此处文档:https://docs.python.org/3.5/library/glob.html#glob.glob - ArtOfWarfaredef 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