web-dev-qa-db-ja.com

Python再帰的なフォルダーの読み取り

私には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()
172
Brock Woolf

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

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)
57
ChillarAnand

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())
34
Clément
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であるかどうかを確認するために使用されます。ファイルの場合は、そのファイルを読み取ることができます。ここにファイルを印刷しています。

4
Neeraj Sonaniya

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

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つのフォルダー、つまりpicsdocsが含まれていること、および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
2
Luc

特定のディレクトリ(シェルのfind .など)の下にあるすべてのパスのフラットリストが必要な場合:

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

ベースディレクトリの下のファイルへのフルパスのみを含めるには、+ subdirsを省略します。

1
Scott Smith

これを試して:

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
1
Diego

問題は、os.walkの出力を正しく処理していないことだと思います。

まず、変更:

filePath = rootdir + '/' + file

に:

filePath = root + '/' + file

rootdirは、固定の開始ディレクトリです。 rootは、os.walkによって返されるディレクトリです。

第二に、ファイル処理ループをインデントする必要はありません。サブディレクトリごとにこれを実行しても意味がないためです。各サブディレクトリにrootが設定されます。ディレクトリ自体で何かをするのでなければ、サブディレクトリを手動で処理する必要はありません。

0
Dave Webb

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
0
b1r3k