Рекурсивная папка Python

У меня есть фон С++/Obj-C, и я просто открываю Python (писал его около часа). Я пишу script для рекурсивного чтения содержимого текстовых файлов в структуре папок.

Проблема, которую я имею, - это код, который я написал, будет работать только для одной папки. Я вижу, почему в коде (см. #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). "Файл" 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 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()

Ответ 2

Если вы используете Python 3.5 или выше, вы можете сделать это в 1 строку.

import glob

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)

Ответ 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

Если вам нужен плоский список всех путей под данным каталогом (например, find. В оболочке):

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

Чтобы включить только полные пути к файлам в базовом каталоге, оставьте out + subdirs.

Ответ 5

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 file или directory, если это файл, то мы можем прочитать этот файл. Здесь я печатаю файл.

Ответ 6

используйте 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()

Ответ 7

TL; DR: Это эквивалентно 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

Итак, он зацикливается три раза, потому что есть три папки: текущая, docs и pics. В каждом цикле он заполняет переменные folders и files всеми папками и файлами. Давайте покажем им:

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

Это показывает нам:

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

Итак, в первой строке мы видим, что мы находимся в папке ., что она содержит две папки, а именно pics и docs, и что существует один файл, а именно 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

Ответ 8

Попробуйте следующее:

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

Ответ 9

Я думаю, проблема в том, что вы не правильно обрабатываете вывод os.walk.

Во-первых, измените:

filePath = rootdir + '/' + file

в

filePath = root + '/' + file

rootdir - ваш фиксированный стартовый каталог; root - это каталог, возвращаемый os.walk.

Во-вторых, вам не нужно отступать от цикла обработки файлов, так как нет смысла запускать его для каждого подкаталога. Вы получите root для каждого подкаталога. Вам не нужно обрабатывать подкаталоги вручную, если вы не хотите что-то делать с самими каталогами.

Ответ 10

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

Ответ 11

Эта функция с указанным родительским directory рекурсивно перебирает все свои directories и prints все имена файлов, внутри которых она найдена. Слишком полезно

import os

def printDirectoryFiles(directory):
   for filename in os.listdir(directory):  
        full_path=os.path.join(directory, filename)
        if not os.path.isdir(full_path): 
            print( full_path + "\n")


def checkFolders(directory):

    dir_list = next(os.walk(directory))[1]

    #print(dir_list)

    for dir in dir_list:           
        print(dir)
        checkFolders(directory +"/"+ dir) 

    printDirectoryFiles(directory)       

main_dir="C:/Users/S0082448/Desktop/carpeta1"

checkFolders(main_dir)


input("Press enter to exit ;")