Python 递归文件夹读取

2025-02-10 08:56:00
admin
原创
54
摘要:问题描述:我有 C++/Obj-C 背景,刚刚开始学习 Python(写了大约一个小时)。我正在编写一个脚本,以递归方式读取文件夹结构中的文本文件的内容。我遇到的问题是,我编写的代码仅适用于一个文件夹深度。我可以从代码中看到原因(请参阅#hardcoded path),但我不知道如何使用 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()

解决方案 1:

确保您理解以下三个返回值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('--
root = ' + 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('    - subdirectory ' + subdir)

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

            print('    - 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:
' % filename).encode('utf-8'))
                list_file.write(f_content)
                list_file.write(b'
')

如果您不知道的话,with文件的语句是一种简写:

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

# is effectively the same as

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

解决方案 2:

如果您使用的是 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)

正如文档中提到的

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

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

import glob

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

解决方案 3:

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

解决方案 4:

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

folder = '.'

import os
for currentpath, folders, files in os.walk(folder):
    for file in files:
        print(os.path.join(currentpath, file))
## or:
import glob
for pathstr in glob.iglob(glob.escape(folder) + '/**/*', recursive=True):
    print(pathstr)

比较两种方法:

  • os.walk速度快 3 倍

  • os.walk在我的测试中,使用了稍微多一点的内存,因为files数组保存了 82k 个条目,而glob返回一个迭代器并流式传输结果。对每个结果的逐个处理(更多调用和更少缓冲)可能解释了速度差异

    • 如果你忘记了iin glob.iglob(),它将返回一个列表而不是迭代器,并且可能会使用更多内存

  • os.walk不会默默地给出不完整的结果或意外地将名称解释匹配模式

  • glob不显示空目录

  • glob需要使用转义目录和文件名,glob.escape(name)因为它们可能包含特殊字符

  • glob排除以点开头的目录和文件(例如,~/.bashrc~/.vim)并且include_hidden不能解决这个问题(它只包括隐藏文件夹;您需要为点文件指定第二种模式)

  • glob没有告诉你什么是文件,什么是目录

  • glob进入符号链接,可能会导致你列举出完全不同位置的大量文件(这可能是你想要的;在这种情况下,os.walk作为followlinks=True一个选项)

  • os.walk让你可以在运行时通过修改文件夹数组来修改要走的路径,不过个人感觉这有点混乱,我不确定我是否会推荐这样做


其他答案已经提到了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

解决方案 5:

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

from pathlib import Path

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

解决方案 6:

我发现以下是最简单的

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)条件可将此列表过滤为仅现有文件

解决方案 7:

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,如果是文件,那么我们可以读取该文件。这里我正在打印文件。

解决方案 8:

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

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

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

解决方案 9:

对我来说,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'
')

解决方案 10:

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

解决方案 11:

如果仅有文件名还不够的话,可以很容易地在其上实现深度优先搜索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() 函数返回目录条目以及文件属性信息,为许多常见用例提供更好的性能。

解决方案 12:

os.walk默认进行递归遍历。对于每个目录,从根目录开始,它会产生一个 3 元组 (dirpath、dirnames、filenames)

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

解决方案 13:

如果你更喜欢(几乎)Oneliner:

from pathlib import Path

lookuppath = '.' #use your path
filelist = [str(item) for item in Path(lookuppath).glob("**/*") if Path(item).is_file()]

在这种情况下,您将获得一个列表,其中仅包含所有文件在 lookuppath 下递归查找的路径。如果没有 str(),您将获得添加到每个路径的 PosixPath()。

解决方案 14:

我认为问题在于您没有正确处理输出os.walk

首先,改变:

filePath = rootdir + '/' + file

到:

filePath = root + '/' + file

rootdir是您的固定起始目录;root是 返回的目录os.walk

其次,您不需要缩进文件处理循环,因为对每个子目录运行此循环毫无意义。您将设置root到每个子目录。除非您想对目录本身进行处理,否则您不需要手动处理子目录。

解决方案 15:

尝试一下:

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

解决方案 16:

这对我有用:

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

解决方案 17:

从 Python 3.12开始,你也可以使用walk()frompathlib类似于os.walk(),但会生成(dirpath, dirnames, filenames)where dirpathis a的元组Path。例如:

from pathlib import Path

for root, dirs, files in Path("cpython/Lib/concurrent").walk(on_error=print):
  print(
      root,
      "consumes",
      sum((root / file).stat().st_size for file in files),
      "bytes in",
      len(files),
      "non-directory files"
  )
  if '__pycache__' in dirs:
        dirs.remove('__pycache__')
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用