Ich habe einen C++/Obj-C-Hintergrund und entdecke gerade Python (schreibe es seit ungefähr einer Stunde) ... Ich schreibe ein Skript, um rekursiv den Inhalt von Textdateien in einer Ordnerstruktur zu lesen.
Mein Problem ist, dass der Code, den ich geschrieben habe, nur für einen Ordner funktioniert. Ich kann sehen, warum im Code (siehe #hardcoded path
) ich einfach nicht weiß, wie ich mit Python fortfahren kann, da meine Erfahrung damit nur brandneu ist.
Python-Code:
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()
Stellen Sie sicher, dass Sie die drei Rückgabewerte von os.walk
verstehen:
for root, subdirs, files in os.walk(rootdir):
hat folgende Bedeutung:
root
: Aktueller Pfad, der "durchlaufen" wirdsubdirs
: Dateien in root
vom Typverzeichnisfiles
: Dateien in root
(nicht in subdirs
) eines anderen Typs als dem VerzeichnisUnd bitte verwenden Sie os.path.join
anstatt mit einem Schrägstrich zu verketten! Ihr Problem ist filePath = rootdir + '/' + file
- Sie müssen den momentan "gelaufenen" Ordner anstelle des obersten Ordners verketten. Das muss also filePath = os.path.join(root, file)
sein. BTW "file" ist eine eingebaute Datei, daher wird sie normalerweise nicht als Variablenname verwendet.
Ein anderes Problem sind Ihre Loops, die zum Beispiel so aussehen sollten:
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')
Wenn Sie nicht wussten, ist die with
-Anweisung für Dateien eine Abkürzung:
with open('filename', 'rb') as f:
dosomething()
# is effectively the same as
f = open('filename', 'rb')
try:
dosomething()
finally:
f.close()
Wenn Sie Python 3.5 oder höher verwenden, können Sie dies in einer Zeile erledigen.
import glob
for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
print(filename)
Wie in der Dokumentation erwähnt
Wenn rekursiv wahr ist, stimmt das Muster '** mit allen Dateien und null oder mehr Verzeichnissen und Unterverzeichnissen überein.
Wenn Sie jede Datei wünschen, können Sie verwenden
import glob
for filename in glob.iglob(root_dir + '**/*', recursive=True):
print(filename)
Stimmen Sie mit Dave Webb überein: os.walk
liefert für jedes Verzeichnis in der Baumstruktur ein Element. Tatsache ist, dass Sie sich nicht um subFolders
kümmern müssen.
Code wie dieser sollte funktionieren:
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())
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())
**/**
wird verwendet, um alle Dateien rekursiv abzurufen, einschließlich directory
.
if os.path.isfile(filename)
wird verwendet, um zu überprüfen, ob filename
variable file
oder directory
ist. Wenn es sich um eine Datei handelt, können wir diese Datei lesen. Hier drucke ich eine Datei.
verwenden Sie os.path.join()
, um Ihre Pfade zu konstruieren.
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()
TL; DR: Dies ist das Äquivalent zu find -type f
, um alle Dateien in allen Verzeichnissen darunter und einschließlich des aktuellen zu durchsuchen:
for currentpath, dirs, files in os.walk('.'):
for file in files:
print(os.path.join(currentpath, file))
Wie bereits in anderen Antworten erwähnt, ist os.walk()
die Antwort, aber es könnte besser erklärt werden. Es ist ganz einfach! Lass uns durch diesen Baum gehen:
docs/
└── doc1.odt
pics/
todo.txt
Mit diesem Code:
for currentpath, folders, files in os.walk('.'):
print(currentpath)
Das currentpath
ist der aktuelle Ordner, in dem es sich befindet. Dies wird Folgendes ausgeben:
.
./docs
./pics
Es wird also dreimal wiederholt, da drei Ordner vorhanden sind: der aktuelle, docs
und pics
. In jeder Schleife werden die Variablen dirs
und files
mit allen Ordnern und Dateien gefüllt. Lassen Sie uns ihnen zeigen:
for currentpath, folders, files in os.walk('.'):
print(currentpath, dirs, files)
Das zeigt uns:
# currentpath folders files
. ['pics', 'docs'] ['todo.txt']
./pics [] []
./docs [] ['doc1.odt']
In der ersten Zeile sehen wir also, dass wir uns im Ordner .
befinden, dass er zwei Ordner enthält, nämlich pics
und docs
, und dass es eine Datei gibt, nämlich todo.txt
. Sie müssen nichts tun, um in diese Ordner zurückzukehren, da sie, wie Sie sehen, automatisch wiederkehren und Ihnen nur die Dateien in Unterordnern anzeigen. Und alle Unterordner davon (obwohl wir diese im Beispiel nicht haben).
Wenn Sie nur alle Dateien durchlaufen möchten, die find -type f
entsprechen, können Sie dies tun:
for currentpath, dirs, files in os.walk('.'):
for file in files:
print(os.path.join(currentpath, file))
Diese gibt aus:
./todo.txt
./docs/doc1.odt
Versuche dies:
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
Ich denke, das Problem ist, dass Sie die Ausgabe von os.walk
nicht korrekt verarbeiten.
Ändern Sie zuerst:
filePath = rootdir + '/' + file
zu:
filePath = root + '/' + file
rootdir
ist Ihr festes Startverzeichnis; root
ist ein von os.walk
zurückgegebenes Verzeichnis.
Zweitens müssen Sie Ihre Dateiverarbeitungsschleife nicht einrücken, da es keinen Sinn macht, dies für jedes Unterverzeichnis auszuführen. Sie erhalten root
für jedes Unterverzeichnis. Sie müssen die Unterverzeichnisse nicht manuell bearbeiten, es sei denn, Sie möchten etwas mit den Verzeichnissen selbst tun.
Wenn Sie eine flache Liste aller Pfade unter einem bestimmten Verzeichnis möchten (wie find .
in der Shell):
files = [
os.path.join(parent, name)
for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
for name in files + subdirs
]
Lassen Sie + subdirs
weg, um nur vollständige Pfade zu Dateien unter dem Basisverzeichnis aufzunehmen.
os.walk
führt standardmäßig rekursive Schritte durch. Ausgehend von root erhält man für jedes Verzeichnis einen 3-Tuple
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