リストpythonでファイルを再帰的に検索してファイルを返す


117

メインフォルダーのサブフォルダーを再帰的に調べ、特定のファイルの種類からリストを作成するスクリプトを作成しています。スクリプトに問題があります。現在、次のように設定されています

for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,subFolder,item))

問題は、subFolder変数が、ITEMファイルが配置されているフォルダーではなく、サブフォルダーのリストを取得していることです。以前にサブフォルダーのforループを実行してパスの最初の部分に参加することを考えていましたが、その前に提案があるかどうかを確認するためにIdをダブルチェックすると考えました。ご協力いただきありがとうございます!

回答:


155

dirpath呼び出すものを使用する必要がありますrootdirnamesあなたが希望していないというフォルダがある場合は、それをプルーニングことができるように供給されているos.walkに再帰するが。

import os
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(PATH) for f in filenames if os.path.splitext(f)[1] == '.txt']

編集:

最新の反対投票の後、私globは拡張機能で選択するためのより良いツールであると思いました。

import os
from glob import glob
result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

ジェネレータ版も

from itertools import chain
result = (chain.from_iterable(glob(os.path.join(x[0], '*.txt')) for x in os.walk('.')))

Python 3.4以降のEdit2

from pathlib import Path
result = list(Path(".").rglob("*.[tT][xX][tT]"))

1
'*。[Tt] [Xx] [Tt]'グロブパターンは、検索で大文字と小文字を区別しません。
SergiyKolesnikov

@SergiyKolesnikovさん、ありがとうございます。下部の編集で使用しました。rglobはWindowsプラットフォームでは区別されないことに注意してください。ただし、移植性はそれほど重要ではありません。
John La Rooy、2018

1
@JohnLaRooyそれも動作しglobます(ここではPython 3.6):glob.iglob(os.path.join(real_source_path, '**', '*.[xX][mM][lL]')
SergiyKolesnikov '29

@Sergiy:iglobサブサブフォルダー以下のファイルでは機能しません。追加する必要がありrecursive=Trueます。
user136036

1
@ user136036、「より良い」は常に最速を意味するわけではありません。読みやすさと保守性も重要な場合があります。
John La Rooy、

111

Python 3.5で変更:「**」を使用した再帰グロブのサポート。

glob.glob()新しい再帰パラメータを取得しました。

.txt以下のすべてのファイルを取得する場合my_path(再帰的にサブディレクトリを含める):

import glob

files = glob.glob(my_path + '/**/*.txt', recursive=True)

# my_path/     the dir
# **/       every file and dir under my_path
# *.txt     every file that ends with '.txt'

イテレータが必要な場合は、代わりにiglobを使用できます。

for file in glob.iglob(my_path, recursive=False):
    # ...

1
TypeError:glob()が予期しないキーワード引数 'recursive'を
取得

1
うまくいくはずです。バージョン3.5以上を使用してください。詳細については、回答にドキュメントへのリンクを追加しました。
Rotareti 16

それが理由です、私は2.7にいます
Cyber​​Jacob

1
なぜリスト内包していないだけfiles = glob.glob(PATH + '/*/**/*.txt', recursive=True)
tobltobs 2017年

おっと!:)それは完全に冗長です。どうしてそんな風に書いたのかわからない。言及してくれてありがとう!直します。
ロタレッティ2017年

20

他の誰かが理解できない場合に備えて、John La Rooyのリスト内包表記をネストされたforのものに翻訳します。

result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

同等である必要があります:

import glob

result = []

for x in os.walk(PATH):
    for y in glob.glob(os.path.join(x[0], '*.txt')):
        result.append(y)

ここでのドキュメントですリストの内包と機能のos.walkglob.globが


1
この答えは、Python 3.7.3ではうまくいきました。glob.glob(..., recursive=True)そして、list(Path(dir).glob(...'))しませんでした。
miguelmorin

11

これは私が思いついた最速のソリューションのようos.walkあり、どのglobソリューションよりもはるかに高速です

  • また、基本的に無料で、すべてのネストされたサブフォルダーのリストが表示されます。
  • 複数の異なる拡張子を検索できます。
  • あなたはまた、変更することで、ファイルのためのいずれかの完全なパスや名前だけを返すように選択することができますf.pathf.name(サブフォルダのためにそれを変更しないでください!)。

引数:dir: str, ext: list
関数 2つのリストを返します:subfolders, files

詳細な速度分析については、以下を参照してください。

def run_fast_scandir(dir, ext):    # dir: str, ext: list
    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files


subfolders, files = run_fast_scandir(folder, [".jpg"])


速度分析

すべてのサブフォルダーとメインフォルダー内の特定のファイル拡張子を持つすべてのファイルを取得するさまざまな方法。

tl; dr:
- fast_scandir明らかに勝利し、os.walkを除く他のすべてのソリューションの2倍の速さです。
- os.walkslighly遅く第二位です。
-を使用globすると、プロセスが大幅に遅くなります。
-どの結果も自然ソートを使用していません。これは、結果が次のようにソートされることを意味します:1、10、2。自然なソート(1、2、10)を取得するには、https://stackoverflow.com/a/48030307/2441026をご覧ください。


結果:

fast_scandir    took  499 ms. Found files: 16596. Found subfolders: 439
os.walk         took  589 ms. Found files: 16596
find_files      took  919 ms. Found files: 16596
glob.iglob      took  998 ms. Found files: 16596
glob.glob       took 1002 ms. Found files: 16596
pathlib.rglob   took 1041 ms. Found files: 16596
os.walk-glob    took 1043 ms. Found files: 16596

テストはW7x64、Python 3.8.1、20実行で行われました。43996個のファイル(部分的にネストされたサブフォルダー)。https://stackoverflow.com/a/45646357/2441026
find_filesからのものであり、いくつかの拡張機能を検索できます。は自分で作成したもので、サブフォルダのリストも返されます。検索する拡張機能のリストを指定できます(私は、単純なものに対して1つのエントリを持つリストをテストしましたが、大きな違いはありませんでした)。
fast_scandirif ... == ".jpg"


# -*- coding: utf-8 -*-
# Python 3


import time
import os
from glob import glob, iglob
from pathlib import Path


directory = r"<folder>"
RUNS = 20


def run_os_walk():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [os.path.join(dp, f) for dp, dn, filenames in os.walk(directory) for f in filenames if
                  os.path.splitext(f)[1].lower() == '.jpg']
    print(f"os.walk\t\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_os_walk_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.jpg'))]
    print(f"os.walk-glob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_glob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = glob(os.path.join(directory, '**', '*.jpg'), recursive=True)
    print(f"glob.glob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_iglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(iglob(os.path.join(directory, '**', '*.jpg'), recursive=True))
    print(f"glob.iglob\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def run_pathlib_rglob():
    a = time.time_ns()
    for i in range(RUNS):
        fu = list(Path(directory).rglob("*.jpg"))
    print(f"pathlib.rglob\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(fu)}")


def find_files(files, dirs=[], extensions=[]):
    # https://stackoverflow.com/a/45646357/2441026

    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1].lower() in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return


def run_fast_scandir(dir, ext):    # dir: str, ext: list
    # https://stackoverflow.com/a/59803793/2441026

    subfolders, files = [], []

    for f in os.scandir(dir):
        if f.is_dir():
            subfolders.append(f.path)
        if f.is_file():
            if os.path.splitext(f.name)[1].lower() in ext:
                files.append(f.path)


    for dir in list(subfolders):
        sf, f = run_fast_scandir(dir, ext)
        subfolders.extend(sf)
        files.extend(f)
    return subfolders, files



if __name__ == '__main__':
    run_os_walk()
    run_os_walk_glob()
    run_glob()
    run_iglob()
    run_pathlib_rglob()


    a = time.time_ns()
    for i in range(RUNS):
        files = []
        find_files(files, dirs=[directory], extensions=[".jpg"])
    print(f"find_files\t\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}")


    a = time.time_ns()
    for i in range(RUNS):
        subf, files = run_fast_scandir(directory, [".jpg"])
    print(f"fast_scandir\ttook {(time.time_ns() - a) / 1000 / 1000 / RUNS:.0f} ms. Found files: {len(files)}. Found subfolders: {len(subf)}")

10

新しいpathlibライブラリはこれを1行に簡略化します。

from pathlib import Path
result = list(Path(PATH).glob('**/*.txt'))

ジェネレーターバージョンを使用することもできます。

from pathlib import Path
for file in Path(PATH).glob('**/*.txt'):
    pass

これはPath、ほとんどすべてに使用できるオブジェクトを返すか、を使用してファイル名を文字列として取得しますfile.name


6

それは最もパイソン的な答えではありませんが、再帰のきちんとしたレッスンであるため、楽しみのためにここに置いておきます

def find_files( files, dirs=[], extensions=[]):
    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1] in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return

私のマシンには2つのフォルダーがrootあり、root2

mender@multivax ]ls -R root root2
root:
temp1 temp2

root/temp1:
temp1.1 temp1.2

root/temp1/temp1.1:
f1.mid

root/temp1/temp1.2:
f.mi  f.mid

root/temp2:
tmp.mid

root2:
dummie.txt temp3

root2/temp3:
song.mid

これらのディレクトリのいずれかで.txtすべての.midファイルを検索したい場合は、次のようにします

files = []
find_files( files, dirs=['root','root2'], extensions=['.mid','.txt'] )
print(files)

#['root2/dummie.txt',
# 'root/temp2/tmp.mid',
# 'root2/temp3/song.mid',
# 'root/temp1/temp1.1/f1.mid',
# 'root/temp1/temp1.2/f.mid']

4

再帰はPython 3.5の新機能であるため、Python 2.7では機能しません。rこれは文字列を使用する例なので、Win、Lin、...

import glob

mypath=r"C:\Users\dj\Desktop\nba"

files = glob.glob(mypath + r'\**\*.py', recursive=True)
# print(files) # as list
for f in files:
    print(f) # nice looking single line per file

注:どれだけ深くても、すべてのファイルがリストされます。


3

このようにして、絶対パスファイルのリストを返すことができます。

def list_files_recursive(path):
    """
    Function that receives as a parameter a directory path
    :return list_: File List and Its Absolute Paths
    """

    import os

    files = []

    # r = root, d = directories, f = files
    for r, d, f in os.walk(path):
        for file in f:
            files.append(os.path.join(r, file))

    lst = [file for file in files]
    return lst


if __name__ == '__main__':

    result = list_files_recursive('/tmp')
    print(result)

2

追加のライトライブラリをインストールしてもかまわない場合は、次のようにできます。

pip install plazy

使用法:

import plazy

txt_filter = lambda x : True if x.endswith('.txt') else False
files = plazy.list_files(root='data', filter_func=txt_filter, is_include_root=True)

結果は次のようになります。

['data/a.txt', 'data/b.txt', 'data/sub_dir/c.txt']

Python 2.7とPython 3の両方で動作します。

Github:https : //github.com/kyzas/plazy#list-files

免責事項:私はの著者ですplazy


1

この関数は、ファイルのみをリストに再帰的に配置します。これがあなたの願いです。

import os


def ls_files(dir):
    files = list()
    for item in os.listdir(dir):
        abspath = os.path.join(dir, item)
        try:
            if os.path.isdir(abspath):
                files = files + ls_files(abspath)
            else:
                files.append(abspath)
        except FileNotFoundError as err:
            print('invalid directory\n', 'Error: ', err)
    return files

0

元のソリューションはほぼ正しいものでしたが、変数「ルート」は再帰的にパスするため、動的に更新されます。os.walk()は再帰的なジェネレーターです。(ルート、サブフォルダー、ファイル)の各タプルセットは、特定のルート用に設定したものです。

すなわち

root = 'C:\\'
subFolder = ['Users', 'ProgramFiles', 'ProgramFiles (x86)', 'Windows', ...]
files = ['foo1.txt', 'foo2.txt', 'foo3.txt', ...]

root = 'C:\\Users\\'
subFolder = ['UserAccount1', 'UserAccount2', ...]
files = ['bar1.txt', 'bar2.txt', 'bar3.txt', ...]

...

全リストを印刷するために、コードを少し調整しました。

import os
for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,item))
            print(fileNamePath)

お役に立てれば!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.