私にはC++/Obj-Cのバックグラウンドがあり、Pythonを発見しているところです(1時間ほど書いています)。フォルダー構造内のテキストファイルの内容を再帰的に読み取るスクリプトを作成しています。
私が持っている問題は、私が書いたコードが1つのフォルダの深さでしか動作しないことです。コードで#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()
os.walk
の3つの戻り値を必ず理解してください。
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()
Python 3.5以上を使用している場合、これを1行で実行できます。
import glob
for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
print(filename)
ドキュメント で述べたように
Recursiveがtrueの場合、パターン「**」はすべてのファイルと0個以上のディレクトリとサブディレクトリに一致します。
すべてのファイルが必要な場合は、使用できます
import glob
for filename in glob.iglob(root_dir + '**/*', recursive=True):
print(filename)
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())
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
であるかどうかを確認するために使用されます。ファイルの場合は、そのファイルを読み取ることができます。ここにファイルを印刷しています。
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()
TL; DR:これは、現在のファイルを含む、以下のすべてのディレクトリ内のすべてのファイルを検索するfind -type f
と同等です。
for currentpath, dirs, 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
の3つのフォルダーがあるため、3回ループします。すべてのループで、変数dirs
およびfiles
をすべてのフォルダーとファイルで埋めます。それらを見せましょう:
for currentpath, folders, files in os.walk('.'):
print(currentpath, dirs, files)
これは次のことを示しています。
# currentpath folders files
. ['pics', 'docs'] ['todo.txt']
./pics [] []
./docs [] ['doc1.odt']
最初の行では、フォルダー.
にあり、2つのフォルダー、つまりpics
とdocs
が含まれていること、および1つのファイル、つまりtodo.txt
があることがわかります。 。ご覧のように、自動的に再帰し、サブフォルダー内のファイルを提供するだけなので、これらのフォルダーに再帰するために何もする必要はありません。そして、そのサブフォルダー(例にはありませんが)。
find -type f
に相当するすべてのファイルをループするだけの場合は、次の操作を実行できます。
for currentpath, dirs, files in os.walk('.'):
for file in files:
print(os.path.join(currentpath, file))
この出力:
./todo.txt
./docs/doc1.odt
特定のディレクトリ(シェルのfind .
など)の下にあるすべてのパスのフラットリストが必要な場合:
files = [
os.path.join(parent, name)
for (parent, subdirs, files) in os.walk(YOUR_DIRECTORY)
for name in files + subdirs
]
ベースディレクトリの下のファイルへのフルパスのみを含めるには、+ subdirs
を省略します。
これを試して:
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
問題は、os.walk
の出力を正しく処理していないことだと思います。
まず、変更:
filePath = rootdir + '/' + file
に:
filePath = root + '/' + file
rootdir
は、固定の開始ディレクトリです。 root
は、os.walk
によって返されるディレクトリです。
第二に、ファイル処理ループをインデントする必要はありません。サブディレクトリごとにこれを実行しても意味がないためです。各サブディレクトリにroot
が設定されます。ディレクトリ自体で何かをするのでなければ、サブディレクトリを手動で処理する必要はありません。
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