os.walk,无需深入研究以下目录


103

如何限制os.walk仅返回提供的目录中的文件?

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList

2
在另一种情况下,多种可能的方法以及随之而来的所有警告说明建议将此功能添加到Python标准库中。
2013年

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]。如果只需要使用文件名f.name而不是f.path。这是最快的解决方案,比任何更快walklistdirstackoverflow.com/a/40347279/2441026
user136036

Answers:


105

使用walklevel功能。

import os

def walklevel(some_dir, level=1):
    some_dir = some_dir.rstrip(os.path.sep)
    assert os.path.isdir(some_dir)
    num_sep = some_dir.count(os.path.sep)
    for root, dirs, files in os.walk(some_dir):
        yield root, dirs, files
        num_sep_this = root.count(os.path.sep)
        if num_sep + level <= num_sep_this:
            del dirs[:]

它的工作方式与相似os.walk,但是您可以向其传递一个level参数,该参数指示递归进行的深度。


3
此功能实际上“遍历”整个结构,然后删除特定点以下的条目吗?还是更聪明的事情发生了?我什至不知道如何用代码检查它。--python初学者
mathtick 2010年

1
@mathtick:当找到所需级别上或以下的某个目录时,其所有子目录都将从子目录列表中删除以进行下一步搜索。这样他们就不会“走”。
nosklo 2010年

2
我刚刚为此+1,因为我在努力“删除”目录。我曾经尝试过dirs = []dirs = None但是那些没有用。 map(dirs.remove, dirs)工作正常,但打印了一些不需要的“ [无]”消息。那么,为什么del dirs[:]特别呢?
Zach Young

4
请注意,topdown=False在os.walk中使用时,此功能无效。请参阅文档的第4段:Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated.
dthor

3
@ZacharyYoung dirs = []dirs = None将无法正常工作,因为它们只是创建了一个新的无关对象并分配给名称dirs。原始列表对象需要就地修改,而不是name dirs
nosklo

206

不要使用os.walk。

例:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item

1
@ 576i:这不能区分文件和目录

4
@Alexandr os.path.isfileos.path.isdir让您与众不同。我不明白,因为os.path.isfile它在08年以来的示例代码中,您的评论来自16年。显然,这是更好的答案,因为您不打算遍历目录而是将其列出。
Daniel F

@DanielF,我的意思是,您需要遍历所有项目,同时walk立即为您提供目录和文件的单独列表。

喔好吧。实际上,Alex的答案似乎更好(使用.next()),并且更接近您的想法。
Daniel F

Python 3.5具有os.scandir允许更复杂的文件或目录对象交互的功能。请参阅下面的答案
作者

48

我认为解决方案实际上非常简单。

break

仅执行for循环的第一次迭代,必须有一种更优雅的方法。

for root, dirs, files in os.walk(dir_name):
    for f in files:
        ...
        ...
    break
...

首次调用os.walk时,它将返回当前目录的郁金香,然后在下一个循环中循环下一个目录的内容。

使用原始脚本,然后添加一个break即可

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
        break
    return outputList

9
这应该是公认的答案。在“ for f in files”循环之后,只需添加“ break”即可停止递归。您可能还需要确保topdown = True。
Alecz

23

使用建议listdir是一个很好的建议。在Python 2中,您的问题的直接答案是root, dirs, files = os.walk(dir_name).next()

等效的Python 3语法是 root, dirs, files = next(os.walk(dir_name))


1
哦,我从那个那里得到了各种各样有趣的错误。ValueError:太多值无法解包
Setori

1
真好!但是,感觉像是黑客。就像您打开引擎但只旋转一圈然后拉开钥匙使其死亡一样。
Daniel F

偶然发现了这一点;root, dirs, files = os.walk(dir_name).next()给我AttributeError: 'generator' object has no attribute 'next'
伊万

3
@Evan,可能是因为它来自2008年,并使用Python 2语法。在Python 3中,您可以编写root, dirs, files = next(os.walk(dir_name)),然后变量root, dirs, files将仅对应于该dir_name级别的生成器变量。
CervEd

13

您可以使用os.listdir()which返回给定目录中的名称列表(包括文件和目录)。如果需要区分文件和目录,请调用os.stat()每个名称。


9

如果您的需求不仅仅是顶层目录(例如,忽略VCS目录等),还可以修改目录列表以防止os.walk在目录中递归。

即:

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        dirs[:] = [d for d in dirs if is_good(d)]
        for f in files:
            do_stuff()

注意-请小心更改列表,而不是重新绑定它。显然,os.walk不了解外部重新绑定。


6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep

4

与的想法相同listdir,但更简短:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]

3

感觉就像在扔我的2便士。

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]

2

在Python 3中,我能够做到这一点:

import os
dir = "/path/to/files/"

#List all files immediately under this folder:
print ( next( os.walk(dir) )[2] )

#List all folders immediately under this folder:
print ( next( os.walk(dir) )[1] )

这也适用于Python2。如何获得第二级?

2

Python 3.5开始,您可以使用os.scandir代替os.listdir。您将获得DirEntry对象的迭代器,而不是字符串。从文档:

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

您可以访问该对象的名称,DirEntry.name然后该名称就相当于该对象的输出os.listdir


1
不仅是“可以”,你用,你应该使用scandir(),因为它是一个很大的速度比listdir()。请参阅此处的基准测试:stackoverflow.com/a/40347279/2441026
user136036

1

您还可以执行以下操作:

for path, subdirs, files in os.walk(dir_name):
    for name in files:
        if path == ".": #this will filter the files in the current directory
             #code here

2
这样是否会不必要地遍历所有子目录和文件?
彼得

0

这就是我解决的方法

if recursive:
    items = os.walk(target_directory)
else:
    items = [next(os.walk(target_directory))]

...

0

使用listdir时有一个陷阱。os.path.isdir(identifier)必须是绝对路径。要选择子目录,请执行以下操作:

for dirname in os.listdir(rootdir):
  if os.path.isdir(os.path.join(rootdir, dirname)):
     print("I got a subdirectory: %s" % dirname)

替代方法是更改​​目录,以在没有os.path.join()的情况下进行测试。


0

您可以使用此代码段

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1

0

创建一个排除列表,使用fnmatch跳过目录结构并执行此过程

excludes= ['a\*\b', 'c\d\e']
for root, directories, files in os.walk('Start_Folder'):
    if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
        for root, directories, files in os.walk(nf_root):
            ....
            do the process
            ....

与“包含”相同:

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):

0

为什么不简单地使用range和并os.walk结合zip?不是最佳解决方案,但也可以。

例如这样:

# your part before
for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
    # logic stuff
# your later part

适用于python 3。

另外:A break更简单。(看@Pieter的答案)


0

亚历克斯的答案略有变化,但使用__next__()

print(next(os.walk('d:/'))[2]) 要么 print(os.walk('d:/').__next__()[2])

[2]作为fileroot, dirs, file其他的答案中提到


0

os.walk找到的每个目录的根文件夹都会更改。我求解器检查根==目录

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        if root == dir_name: #This only meet parent folder
            for f in files:
                if os.path.splitext(f)[1] in whitelist:
                    outputList.append(os.path.join(root, f))
                else:
                    self._email_to_("ignore")
    return outputList

0
import os

def listFiles(self, dir_name):
    names = []
    for root, directory, files in os.walk(dir_name):
        if root == dir_name:
            for name in files:
                names.append(name)
    return names

1
嗨,Rich,欢迎来到Stack Overflow!感谢您提供此代码段,它可能会提供一些有限的短期帮助。通过说明为什么这是一个解决问题的好方法,适当的解释将大大提高其长期价值,对于将来有其他类似问题的读者来说,这将更为有用。请编辑您的答案以添加一些解释,包括您所做的假设。
kenny_k
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.