Python递归文件夹读取

Bro*_*olf 189 python scripting file-io

我有一个C++/Obj-C背景,我只是发现了Python(已经写了大约一个小时).我正在编写一个脚本,以递归方式读取文件夹结构中的文本文件的内容.

我遇到的问题是我编写的代码只适用于一个文件夹.我可以在代码中看到原因(参见参考资料#hardcoded path),我只是不知道如何继续使用Python,因为我的经验只是全新的.

Python代码:

import os
import sys

rootdir = sys.argv[1]

for root, subFolders, files in os.walk(rootdir):

    for folder in subFolders:
        outfileName = rootdir + "/" + folder + "/py-outfile.txt" # hardcoded path
        folderOut = open( outfileName, 'w' )
        print "outfileName is " + outfileName

        for file in files:
            filePath = rootdir + '/' + file
            f = open( filePath, 'r' )
            toWrite = f.read()
            print "Writing '" + toWrite + "' to" + filePath
            folderOut.write( toWrite )
            f.close()

        folderOut.close()
Run Code Online (Sandbox Code Playgroud)

And*_*Dog 310

确保您了解以下三个返回值os.walk:

for root, subdirs, files in os.walk(rootdir):
Run Code Online (Sandbox Code Playgroud)

具有以下含义:

  • root:当前路径"走过"
  • subdirs:root类型目录中的文件
  • files:除目录以外的类型root(非subdirs)的文件

请使用os.path.join而不是用斜线连接!您的问题是filePath = rootdir + '/' + file- 您必须连接当前"walked"文件夹而不是最顶层的文件夹.所以那一定是filePath = os.path.join(root, file).BTW"文件"是内置的,因此您通常不会将其用作变量名.

另一个问题是你的循环,它应该是这样的,例如:

import os
import sys

walk_dir = sys.argv[1]

print('walk_dir = ' + walk_dir)

# If your current working directory may change during script execution, it's recommended to
# immediately convert program arguments to an absolute path. Then the variable root below will
# be an absolute path as well. Example:
# walk_dir = os.path.abspath(walk_dir)
print('walk_dir (absolute) = ' + os.path.abspath(walk_dir))

for root, subdirs, files in os.walk(walk_dir):
    print('--\nroot = ' + root)
    list_file_path = os.path.join(root, 'my-directory-list.txt')
    print('list_file_path = ' + list_file_path)

    with open(list_file_path, 'wb') as list_file:
        for subdir in subdirs:
            print('\t- subdirectory ' + subdir)

        for filename in files:
            file_path = os.path.join(root, filename)

            print('\t- file %s (full path: %s)' % (filename, file_path))

            with open(file_path, 'rb') as f:
                f_content = f.read()
                list_file.write(('The file %s contains:\n' % filename).encode('utf-8'))
                list_file.write(f_content)
                list_file.write(b'\n')
Run Code Online (Sandbox Code Playgroud)

如果你不知道,with文件的声明是一个简写:

with open('filename', 'rb') as f:
    dosomething()

# is effectively the same as

f = open('filename', 'rb')
try:
    dosomething()
finally:
    f.close()
Run Code Online (Sandbox Code Playgroud)

  • 像我这样愚蠢/遗忘的任何人......这个代码示例将txt文件写入每个目录.很高兴我在一个受版本控制的文件夹中对它进行了测试,尽管编写清理脚本所需的一切也在这里:) (12认同)
  • 精湛的,大量的印刷品,以了解正在发生的事情,它的工作完美.谢谢!+1 (4认同)

Chi*_*and 76

如果您使用的是Python 3.5或更高版本,则可以在1行中完成此操作.

import glob

for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
     print(filename)
Run Code Online (Sandbox Code Playgroud)

文档中所述

如果recursive为true,则模式"**"将匹配任何文件以及零个或多个目录和子目录.

如果你想要每个文件,你可以使用

import glob

for filename in glob.iglob(root_dir + '**/*', recursive=True):
     print(filename)
Run Code Online (Sandbox Code Playgroud)

  • root_dir必须带有斜杠(否则,您将获得类似于“ folder ** / *”的内容,而不是“ folder / ** / *”作为第一个参数)。您可以使用os.path.join(root_dir,'** / *'),但我不知道将os.path.join与通配符路径一起使用是否可以接受(尽管它适用于我的应用程序)。 (7认同)
  • 如果我按照答案运行它,它就不会递归地工作。为了使这项工作递归地进行,我必须将其更改为:`glob.iglob(root_dir + '**/**', recursive=True)`。我正在 Python 3.8.2 中工作 (4认同)
  • 请注意,glob.glob 与点文件不匹配。您可以使用 pathlib.glob 代替 (3认同)
  • 只需很短的代码就可以了 (2认同)

Clé*_*ent 35

同意Dave Webb,os.walk将为树中的每个目录生成一个项目.事实是,你只是不必关心subFolders.

像这样的代码应该工作:

import os
import sys

rootdir = sys.argv[1]

for folder, subs, files in os.walk(rootdir):
    with open(os.path.join(folder, 'python-outfile.txt'), 'w') as dest:
        for filename in files:
            with open(os.path.join(folder, filename), 'r') as src:
                dest.write(src.read())
Run Code Online (Sandbox Code Playgroud)

  • 好一个.这也有效.然而,我更喜欢AndiDog的版本,即使它更长,因为作为Python的初学者更清楚地理解它.+1 (3认同)

Luc*_*Luc 27

TL;DR:这相当于find -type f遍历下面所有文件夹中的所有文件,包括当前文件夹:

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))
Run Code Online (Sandbox Code Playgroud)

正如其他答案中已经提到的那样,os.walk()是答案,但可以更好地解释。这很简单!让我们穿过这棵树:

docs/
??? doc1.odt
pics/
todo.txt
Run Code Online (Sandbox Code Playgroud)

使用此代码:

for currentpath, folders, files in os.walk('.'):
    print(currentpath)
Run Code Online (Sandbox Code Playgroud)

currentpath是它正在寻找在当前文件夹。这将输出:

.
./docs
./pics
Run Code Online (Sandbox Code Playgroud)

所以它循环了 3 次,因为有 3 个文件夹:当前文件夹、docs、 和pics。在每个循环中,它都会填充变量folders以及files所有文件夹和文件。让我们向他们展示:

for currentpath, folders, files in os.walk('.'):
    print(currentpath, folders, files)
Run Code Online (Sandbox Code Playgroud)

这向我们展示了:

# currentpath  folders           files
.              ['pics', 'docs']  ['todo.txt']
./pics         []                []
./docs         []                ['doc1.odt']
Run Code Online (Sandbox Code Playgroud)

所以在第一行,我们看到我们在文件夹中.,它包含两个文件夹,即picsdocs,还有一个文件,即todo.txt. 您无需执行任何操作即可递归到这些文件夹中,因为正如您所见,它会自动递归并只为您提供任何子文件夹中的文件。以及它的任何子文件夹(尽管我们在示例中没有这些)。

如果您只想遍历所有文件,相当于find -type f,您可以这样做:

for currentpath, folders, files in os.walk('.'):
    for file in files:
        print(os.path.join(currentpath, file))
Run Code Online (Sandbox Code Playgroud)

这输出:

./todo.txt
./docs/doc1.odt
Run Code Online (Sandbox Code Playgroud)


cho*_*rbs 11

pathlib库非常适合处理文件。您可以Path像这样对对象执行递归全局操作。

from pathlib import Path

for elem in Path('/path/to/my/files').rglob('*.*'):
    print(elem)
Run Code Online (Sandbox Code Playgroud)


Sco*_*ith 6

如果要给定目录下所有路径的平面列表(如find .在shell中):

   files = [ 
       os.path.join(parent, name)
       for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
       for name in files + subdirs
   ]
Run Code Online (Sandbox Code Playgroud)

要仅在基本目录下包含文件的完整路径,请省略+ subdirs


Nee*_*iya 6

import glob
import os

root_dir = <root_dir_here>

for filename in glob.iglob(root_dir + '**/**', recursive=True):
    if os.path.isfile(filename):
        with open(filename,'r') as file:
            print(file.read())
Run Code Online (Sandbox Code Playgroud)

**/**用于递归获取所有文件,包括directory

if os.path.isfile(filename)用于检查filename变量是file还是directory,如果它是文件,那么我们可以读取该文件。我在这里打印文件。


Mic*_*ein 6

我发现以下是最简单的

from glob import glob
import os

files = [f for f in glob('rootdir/**', recursive=True) if os.path.isfile(f)]
Run Code Online (Sandbox Code Playgroud)

使用glob('some/path/**', recursive=True)获取所有文件,但也包括目录名称。添加if os.path.isfile(f)条件仅将此列表过滤到现有文件


Gwa*_*Kim 5

对我来说os.walk()有点太复杂和冗长了。您可以通过以下方式使接受的答案更清晰:

all_files = [str(f) for f in pathlib.Path(dir_path).glob("**/*") if f.is_file()]

with open(outfile, 'wb') as fout:
    for f in all_files:
        with open(f, 'rb') as fin:
            fout.write(fin.read())
            fout.write(b'\n')
Run Code Online (Sandbox Code Playgroud)