Glob.glob modülünü kullanarak alt klasörleri nasıl arayabilirim?


107

Bir klasörde bir dizi alt klasör açmak ve bazı metin dosyaları bulmak ve metin dosyalarının bazı satırlarını yazdırmak istiyorum. Bunu kullanıyorum:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

Ancak bu, alt klasörlere de erişemez. Alt klasörlere erişmek için aynı komutu nasıl kullanabileceğimi bilen var mı?


Yanıtlar:


163

Python 3.5 ve daha yeni sürümlerde, yeni özyinelemeli **/işlevselliği kullanın :

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

Ayarlandığında recursive, **ardından bir yol ayırıcısı 0 veya daha fazla alt dizinle eşleşir.

Önceki Python sürümlerinde, glob.glob()alt dizinlerdeki dosyaları özyinelemeli olarak listeleyemez.

Bu durumda os.walk(), fnmatch.filter()bunun yerine şunu birlikte kullanırdım:

import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Bu, dizinlerinizi yinelemeli olarak gezer ve tüm mutlak yol adlarını eşleşen .txtdosyalara döndürür . Bu özel durumda fnmatch.filter()aşırı olabilir, ayrıca bir .endswith()test de kullanabilirsiniz :

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]

3
Görebiliyorum: glob.glob ('/ dizine giden yol / * / *. Txt ") benim için çalışıyor. Bu temelde Unix kabuk kuralını kullanıyor.
Surya

7
@ User123: dizinleri yinelemeli olarak listelemez . Tüm metin dosyalarını bir seviye derinliğinde listeliyorsunuz , ancak daha fazla alt dizinlerde veya hatta doğrudan path to directory.
Martijn Pieters

1
Bu tamamen alakalı değildir, ancak neden işlevsellikle recursive=Falsebirlikte ayarlamak **/yalnızca verilen klasördeki dosyaların listesini değil, alt bileşenlerini sağlar?
Dr_Zaszuś

@ Dr_Zaszuś: pardon? mevcut çalışma dizininde dizin adlarının**/ bir listesini verir , çünkü desen biter ve temelde sizinle aynı eşleşen , sadece daha az verimli olan bir çiftiniz olur. /recursive=False**/
Martijn Pieters

@ Dr_Zaszuś: */*tüm alt dizinlerdeki tüm dosyalara ihtiyacınız varsa kullanın .
Martijn Pieters

22

Hemen alt dizinlerdeki dosyaları bulmak için:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

Tüm alt dizinleri dolaşan yinelemeli bir sürüm için, Python** 3.5'ten recursive=True beri kullanabilir ve geçebilirsiniz :

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

Her iki işlev çağrısı da listeleri döndürür. glob.iglob()Yolları tek tek döndürmek için kullanabilirsiniz . Veya şunu kullanınpathlib :

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

Her iki yöntem de yineleyiciler döndürür (yolları tek tek alabilirsiniz).


Evet anladım; ancak glob()dizinlerde kalıpları desteklemeyi de beklemiyordum .
Martijn Pieters

Yorum silindi, şimdi yanlış bir izlenim verdiğini görüyorum; ayrıca yama, **özyineleme durumu için bir belge güncellemesi içerir . Ama için **işe, sen sahip ayarlamak için recursion=Truebtw anahtarı.
Martijn Pieters

20

Bu konuda çok fazla kafa karışıklığı var. Bakalım açıklayabilecek miyim (Python 3.7):

  1. glob.glob('*.txt') :geçerli dizinde '.txt' ile biten tüm dosyalarla eşleşir
  2. glob.glob('*/*.txt') :1 ile aynı
  3. glob.glob('**/*.txt') :yalnızca yakın alt dizinlerde '.txt' ile biten tüm dosyalarla eşleşir , ancak geçerli dizinde eşleşmez
  4. glob.glob('*.txt',recursive=True) :1 ile aynı
  5. glob.glob('*/*.txt',recursive=True) :3 ile aynı
  6. glob.glob('**/*.txt',recursive=True):geçerli dizinde ve tüm alt dizinlerde '.txt' ile biten tüm dosyalarla eşleşir

Bu yüzden her zaman belirtmek en iyisidir recursive=True.


1
Bu en iyi cevap olmalı!
Abhik Sarkar

17

Glob2 paket yabani kartlarını destekler ve oldukça hızlı

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

Dizüstü bilgisayarımda > 60.000 dosya yolunun eşleşmesi yaklaşık 2 saniye sürüyor .


9

Formic'i Python 2.6 ile kullanabilirsiniz

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

Açıklama - Bu paketin yazarıyım.


4

İşte glob.globkullanmadan benzer işlevselliği sağlayan uyarlanmış bir sürüm glob2.

def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

Yani aşağıdaki dizin yapısına sahipseniz

tests/files
├── a0
   ├── a0.txt
   ├── a0.yaml
   └── b0
       ├── b0.yaml
       └── b00.yaml
└── a1

Bunun gibi bir şey yapabilirsin

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

fnmatchYalnızca dosya adı yerine tüm dosya adında hemen hemen kalıp eşleşmesi.


2

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

Her durumda işe yaramaz, bunun yerine glob2'yi kullanın

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")

2

Glob2 paketini kurabilirseniz ...

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

Tüm dosya adları ve klasörler:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  

2

Python 3.4+ çalıştırıyorsanız, pathlibmodülü kullanabilirsiniz . Path.glob()Yöntem destekler **deseni, “yinelemeli bu dizin ve tüm alt dizinleri,” anlamına gelir. PathTüm eşleşen dosyalar için nesneler veren bir jeneratör döndürür .

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")

0

Martijn'in belirttiği gibi, glob bunu yalnızca **Python 3.5'te tanıtılan operatör aracılığıyla yapabilir . OP açıkça glob modülünü istediğinden, aşağıdakiler benzer şekilde davranan tembel bir değerlendirme yineleyicisi döndürecektir

import os, glob, itertools

configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

configfilesBu yaklaşımda yalnızca bir kez yineleme yapabileceğinizi unutmayın . Birden çok işlemde kullanılabilecek gerçek bir yapılandırma dosyası listesine ihtiyacınız varsa, bunu kullanarak açıkça oluşturmanız gerekir list(configfiles).


0

Komuta rglob , dizin yapınızın en derin alt seviyesinde sonsuz bir özyineleme yapacak. Sadece bir seviye derinliğini istiyorsanız, o zaman kullanmayın.

OP'nin glob.glob'u kullanmaktan bahsettiğini fark ettim. Ancak bunun, tüm alt klasörleri yinelemeli olarak arama amacına yanıt verdiğine inanıyorum.

rglobFonksiyonu son okuma verileri için sabit bir varsayım olarak klasör yapısı kullanılarak bir veri işleme algoritması için bir hız 100x artışa neden oldu. Bununla birlikte, rglobbelirli bir üst dizindeki veya altındaki tüm dosyalar arasında bir kez tek bir tarama yapabildik, adlarını bir listeye (bir milyondan fazla dosya) kaydedip, ardından bu listeyi herhangi bir dosyada hangi dosyaları açmamız gerektiğini belirlemek için kullanabildik. gelecekte, yalnızca dosya adlandırma kurallarına göre hangi klasörde bulunduklarına göre gelin.

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.