Python递归文件夹读取

我有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()
399071 次浏览

我认为问题是你没有正确地处理os.walk的输出。

首先,改变:

filePath = rootdir + '/' + file

:

filePath = root + '/' + file

rootdir是固定的起始目录;rootos.walk返回的目录。

其次,您不需要缩进您的文件处理循环,因为对每个子目录运行这个没有意义。你会得到root设置到每个子目录。您不需要手动处理子目录,除非您想对目录本身做一些事情。

使用os.path.join()来构造路径-这样更整洁:

import os
import sys
rootdir = sys.argv[1]
for root, subFolders, files in os.walk(rootdir):
for folder in subFolders:
outfileName = os.path.join(root,folder,"py-outfile.txt")
folderOut = open( outfileName, 'w' )
print "outfileName is " + outfileName
for file in files:
filePath = os.path.join(root,file)
toWrite = open( filePath).read()
print "Writing '" + toWrite + "' to" + filePath
folderOut.write( toWrite )
folderOut.close()

确保你理解了os.walk的三个返回值:

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

具有以下含义:

  • root:当前路径被“遍历”
  • subdirs:目录类型为root的文件
  • files:在root(不是在subdirs)中的非目录类型的文件

并且请使用os.path.join而不是用斜杠连接!你的问题是filePath = rootdir + '/' + file -你必须连接当前“行走”的文件夹,而不是最上面的文件夹。所以它必须是filePath = os.path.join(root, file)。顺便说一句,“文件”是内置的,所以你通常不使用它作为变量名。

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

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

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

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


# is effectively the same as


f = open('filename', 'rb')
try:
dosomething()
finally:
f.close()

同意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())

os.walk默认执行递归遍历。对于每个dir,它从根目录开始生成一个3元组(dirpath, dirnames, filename)

from os import walk
from os.path import splitext, join


def select_files(root, files):
"""
simple logic here to filter out interesting files
.py files in this example
"""


selected_files = []


for file in files:
#do concatenation here to get full path
full_path = join(root, file)
ext = splitext(file)[1]


if ext == ".py":
selected_files.append(full_path)


return selected_files


def build_recursive_dir_tree(path):
"""
path    -    where to begin folder scan
"""
selected_files = []


for root, dirs, files in walk(path):
selected_files += select_files(root, files)


return selected_files

试试这个:

import os
import sys


for root, subdirs, files in os.walk(path):


for file in os.listdir(root):


filePath = os.path.join(root, file)


if os.path.isdir(filePath):
pass


else:
f = open (filePath, 'r')
# Do Stuff

如果你使用的是Python 3.5或更高版本,你可以在一行内完成。

import glob


# root_dir needs a trailing slash (i.e. /root/dir/)
for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
print(filename)

文档中所述

如果递归为true,模式'**'将匹配任何文件以及零个或多个目录和子目录。

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

import glob


for filename in glob.iglob(root_dir + '**/**', recursive=True):
print(filename)

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

   files = [
os.path.join(parent, name)
for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
for name in files + subdirs
]

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

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

**/**用于递归获取包括directory在内的所有文件。

if os.path.isfile(filename)用于检查filename变量是否为filedirectory,如果是文件,则可以读取该文件。 这里我正在打印文件

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

for currentpath, folders, files in os.walk('.'):
for file in files:
print(os.path.join(currentpath, file))

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

docs/
└── doc1.odt
pics/
todo.txt

下面的代码:

for currentpath, folders, files in os.walk('.'):
print(currentpath)

currentpath是它正在查看的当前文件夹。这将输出:

.
./docs
./pics

因此它循环了三次,因为有三个文件夹:当前文件夹、docspics。在每个循环中,它用所有文件夹和文件填充变量foldersfiles。让我们向他们展示:

for currentpath, folders, files in os.walk('.'):
print(currentpath, folders, files)

这告诉我们:

# currentpath  folders           files
.              ['pics', 'docs']  ['todo.txt']
./pics         []                []
./docs         []                ['doc1.odt']

所以在第一行中,我们看到我们在文件夹.中,它包含两个文件夹,即picsdocs,并且有一个文件,即todo.txt。你不需要做任何事情递归到这些文件夹中,因为如你所见,它会自动递归给你任何子文件夹中的文件。以及它的任何子文件夹(尽管在示例中没有)。

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

for currentpath, folders, files in os.walk('.'):
for file in files:
print(os.path.join(currentpath, file))

这个输出:

./todo.txt
./docs/doc1.odt

pathlib库非常适合处理文件。你可以像这样对Path对象进行递归glob。

from pathlib import Path


for elem in Path('/path/to/my/files').rglob('*.*'):
print(elem)

我发现下面的方法是最简单的

from glob import glob
import os


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

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

如果你更喜欢(几乎)联机:

from pathlib import Path


lookuppath = '.' #use your path
filelist = [str(item) for item in Path(lookuppath).glob("**/*") if Path(item).is_file()]
在这种情况下,您将获得一个列表,其中仅包含递归位于lookppath下的所有文件的路径。 如果没有str(),你将得到PosixPath()添加到每个路径

这招对我很管用:

import glob


root_dir = "C:\\Users\\Scott\\" # Don't forget trailing (last) slashes
for filename in glob.iglob(root_dir + '**/*.jpg', recursive=True):
print(filename)
# do stuff

在我看来,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')

如果只有文件名还不够,很容易在os.scandir()之上实现深度优先搜索:

stack = ['.']
files = []
total_size = 0
while stack:
dirname = stack.pop()
with os.scandir(dirname) as it:
for e in it:
if e.is_dir():
stack.append(e.path)
else:
size = e.stat().st_size
files.append((e.path, size))
total_size += size

文档是这样说的:

scandir()函数返回目录条目和文件属性信息,为许多常见用例提供了更好的性能。