Python özyinelemeli klasör okuma


225

Bir C ++ / Obj-C arka plan var ve sadece Python (yaklaşık bir saattir yazıyorum) keşfediyorum. Bir klasör yapısında metin dosyalarının içeriğini tekrar tekrar okumak için bir komut dosyası yazıyorum.

Ben sorun yazdım kod sadece bir klasör derin için çalışacak olmasıdır. Neden kodda görebilirsiniz (bkz. #hardcoded path), Sadece benim deneyim sadece yepyeni çünkü Python ile nasıl ilerleyebilir bilmiyorum.

Python Kodu:

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

Yanıtlar:


348

Aşağıdaki üç dönüş değerini anladığınızdan emin olun os.walk:

for root, subdirs, files in os.walk(rootdir):

şu anlama gelir:

  • root: "İçinden geçilen" geçerli yol
  • subdirs: rootTür dizinindeki dosyalar
  • files: Dizin dışındaki türdeki root(içinde olmayan subdirs) dosyalar

Ve lütfen os.path.joineğik çizgi ile birleştirmek yerine kullanın ! Sorununuz filePath = rootdir + '/' + file- en üstteki klasör yerine şu anda yürürlükte olan "klasörü" birleştirmeniz gerekir. Öyleyse öyle olmalı filePath = os.path.join(root, file). BTW "file" bir yerleşiktir, bu nedenle normalde değişken adı olarak kullanmazsınız.

Başka bir sorun, döngülerinizdir, bu böyle olmalıdır, örneğin:

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')

Eğer bilmiyorsanız, withdosyalar için ifade bir kısayoldur:

with open('filename', 'rb') as f:
    dosomething()

# is effectively the same as

f = open('filename', 'rb')
try:
    dosomething()
finally:
    f.close()

4
Neler olduğunu anlamak için mükemmel, çok sayıda baskı ve mükemmel çalışıyor. Teşekkürler! +1
Brock Woolf

16
Benim gibi aptal / habersiz olan herkese yönelir ... bu kod örneği her dizine bir txt dosyası yazar. Sevindim Ben bir temizleme komut dosyası yazmak için gereken her şey de burada olsa da, sürüm kontrollü bir klasörde test :)
Steazy

o ikinci (en uzun) kod snippet'i çok iyi çalıştı, beni çok sıkıcı işten kurtardı
amphibient

1
Açıkçası en önemli yönü ise hız os.walkkötü olmadığından, daha hızlı bir yol buldum os.scandir. Tüm globçözümler walk& ' dan çok daha yavaştır scandir.
İşlevim

112

Python 3.5 veya üstünü kullanıyorsanız, bunu 1 satırda yapabilirsiniz.

import glob

for filename in glob.iglob(root_dir + '**/*.txt', recursive=True):
     print(filename)

Belgelerde belirtildiği gibi

Özyinelemeli doğruysa, '**' kalıbı herhangi bir dosya ve sıfır veya daha fazla dizin ve alt dizinle eşleşir.

Her dosyayı istiyorsanız,

import glob

for filename in glob.iglob(root_dir + '**/*', recursive=True):
     print(filename)

TypeError: iglob (), 'özyinelemeli' beklenmeyen bir anahtar kelime argümanı aldı
Jewenile

1
Başlangıçta belirtildiği gibi, sadece Python 3.5+
ChillarAnand

9
root_dir'de bir eğik çizgi olmalıdır (aksi takdirde ilk argüman olarak 'klasör / ** / *' yerine 'klasör ** / *' gibi bir şey elde edersiniz). Os.path.join (root_dir, ' * / ') kullanabilirsiniz, ancak joker yolları ile os.path.join kullanmanın kabul edilebilir olup olmadığını bilmiyorum (yine de benim uygulama için çalışır).
drojf

@ChillarAnand Bu cevaba root_dirsondaki eğik çizgi gerektiren koda bir yorum ekleyebilir misiniz ? Bu insanlara zaman kazandıracak (ya da en azından bana zaman kazandıracak). Teşekkürler.
Dan Nissenbaum

1
Eğer cevabı olduğu gibi çalıştırırsam özyineli olarak çalışmadı. Ben bunu değiştirmek zorunda yinelemeli Bu işi yapmak için: glob.iglob(root_dir + '**/**', recursive=True). Python 3.8.2'de çalışıyorum
mikey

38

Dave Webb ile os.walkaynı fikirde olmak, ağaçtaki her dizin için bir öğe verecektir. Gerçek şu ki, sadece önemsemek zorunda değilsiniz subFolders.

Bunun gibi kod çalışması gerekir:

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

3
Güzel bir. Bu da işe yarıyor. Ancak daha uzun olmasına rağmen AndiDog'un sürümünü tercih ediyorum çünkü Python'a yeni başlayan biri olarak anlaşılması daha açık. +1
Brock Woolf

20

TL; DR: Bu, find -type fşu anki klasör de dahil olmak üzere aşağıdaki tüm klasörlerde bulunan tüm dosyaların üzerinden geçmeye eşdeğerdir :

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

Diğer cevaplarda daha önce bahsedildiği gibi os.walk(), cevaptır, ancak daha iyi açıklanabilir. Oldukça basit! Bu ağaçtan geçelim:

docs/
└── doc1.odt
pics/
todo.txt

Bu kodla:

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

Baktığı currentpathgeçerli klasördür. Bu çıktı:

.
./docs
./pics

Bu yüzden üç kez döngüye girer, çünkü üç klasör vardır: geçerli klasör docs, ve pics. Her döngüde, değişkenleri foldersve filestüm klasör ve dosyaları doldurur . Onlara gösterelim:

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

Bu bize şunu gösterir:

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

İlk satırda Yani, biz klasörde olduğunu görmek .o yani iki klasör içerdiğini, picsve docsyani bir dosya bulunmaktadır ve bu todo.txt. Gördüğünüz gibi, otomatik olarak geri çekilir ve size herhangi bir alt klasördeki dosyaları verir, çünkü bu klasörleri geri almak için hiçbir şey yapmanıza gerek yoktur. Ve bunun herhangi bir alt klasörü (örnekteki gibi olmasa da).

Eşdeğeri olan tüm dosyalar arasında döngü find -type fyapmak istiyorsanız, bunu yapabilirsiniz:

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

Bu çıktılar:

./todo.txt
./docs/doc1.odt

9

pathlibKütüphane dosyaları ile çalışmak için gerçekten harika. Böyle bir Pathnesne üzerinde özyinelemeli bir küre yapabilirsiniz .

from pathlib import Path

for elem in Path('/path/to/my/files').rglob('*.*'):
    print(elem)

6

Belirli bir dizin altındaki tüm yolların düz bir listesini istiyorsanız ( find .kabuktaki gibi ):

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

Yalnızca base dir altındaki dosyalara tam yol eklemek için dışarıda bırakın + subdirs.


6
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())

**/**dahil tüm dosyaları özyinelemeli olarak almak için kullanılır directory.

if os.path.isfile(filename)filenamedeğişkeni olup olmadığını kontrol etmek için kullanılır fileveya directorydosya ise o dosyayı okuyabiliriz. Burada dosya yazdırıyorum.


6

En kolayı olarak aşağıdakileri buldum

from glob import glob
import os

files = [f for f in glob('rootdir/**', recursive=True) if os.path.isfile(f)]

Kullanımı glob('some/path/**', recursive=True)tüm dosyaları alır, ancak dizin adlarını da içerir. if os.path.isfile(f)Koşul eklemek, bu listeyi yalnızca mevcut dosyalara filtreler


3

kullanım os.path.join()yollarınızı oluşturmak için - Daha temiz:

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

Bu kod sadece klasör 2 seviyeleri (veya daha derin) için çalışıyor gibi görünüyor. Hala beni yaklaştırıyor.
Brock Woolf

1

os.walkvarsayılan olarak yinelemeli yürüyüş yapar. Her bir direk için kökten başlayarak 3 bir demet verir (dirpath, dirnames, dosya adları)

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

1
Python 2.6 olarak walk() do özyinelemeli listesini döndürür. Kodunuzu denedim ve birçok tekrarlı bir liste aldım ... Eğer sadece "# alt klasörlerde özyinelemeli çağrılar" yorumunun altındaki satırları kaldırırsanız - iyi çalışır
borisbn

1

Bunu dene:

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

Dizin listesini walk () dizininden dosyalara ve dizinlere ayırdığınızda neden başka bir listdir () ve ardından isdir () yapasınız? Bu, büyük ağaçlarda oldukça yavaş görünecek gibi görünüyor (bir yerine üç sistem çağrısı yapın: 1 = yürüyüş, 2 = listdir, 3 = 'altdiziler ve' dosyalar 'arasında yürümek ve döngü yapmak yerine).
Luc

0

Bence sorun çıktıyı os.walkdoğru işlemiyor olmanız .

İlk olarak, değiştirin:

filePath = rootdir + '/' + file

için:

filePath = root + '/' + file

rootdirsabit başlangıç ​​dizininiz; roottarafından döndürülen bir dizindir os.walk.

İkinci olarak, bunu her alt dizin için çalıştırmak mantıklı olmadığından dosya işleme döngünüzü girintilemeniz gerekmez. Sen alırsınız roother alt dizine set. Dizinlerin kendileriyle bir şey yapmak istemiyorsanız alt dizinleri el ile işlemeniz gerekmez.


Her alt dizinde veri var, bu yüzden her dizinin içeriği için ayrı bir metin dosyası olması gerekir.
Brock Woolf

@Brock: dosya bölümü geçerli dizindeki dosyaların listesidir. Yani girinti gerçekten yanlış. Yazıyorsunuz filePath = rootdir + '/' + file, bu doğru görünmüyor: dosya geçerli dosyalar listesinden, yani mevcut birçok dosyaya mı yazıyorsunuz?
Alok Singhal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.