Bagaimana cara mencari sub-folder menggunakan modul glob.glob?


107

Saya ingin membuka serangkaian subfolder dalam sebuah folder dan menemukan beberapa file teks dan mencetak beberapa baris file teks tersebut. Saya menggunakan ini:

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

Tetapi ini juga tidak dapat mengakses subfolder. Adakah yang tahu bagaimana saya dapat menggunakan perintah yang sama untuk mengakses subfolder juga?


Jawaban:


163

Di Python 3.5 dan yang lebih baru, gunakan **/fungsi rekursif baru :

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

Saat recursivedisetel, **diikuti dengan pemisah jalur yang cocok dengan 0 atau lebih subdirektori.

Dalam versi Python sebelumnya, glob.glob()tidak dapat mencantumkan file di subdirektori secara rekursif.

Dalam hal ini saya akan menggunakan os.walk()kombinasi dengan fnmatch.filter():

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

Ini akan menjalankan direktori Anda secara rekursif dan mengembalikan semua nama jalur absolut ke .txtfile yang cocok . Dalam kasus khusus ini yang fnmatch.filter()mungkin berlebihan, Anda juga bisa menggunakan .endswith()tes:

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
Saya dapat melihat: glob.glob ('/ path to directory / * / *. Txt ") berfungsi untuk saya. Ini pada dasarnya menggunakan aturan shell Unix.
Surya

7
@ User123: itu tidak mencantumkan direktori secara rekursif . Anda mencantumkan semua file teks sedalam satu tingkat , tetapi tidak di subdirektori lebih lanjut atau bahkan langsung di path to directory.
Martijn Pieters

1
Ini tidak sepenuhnya terkait, tetapi mengapa pengaturan recursive=Falsebersama dengan **/ fungsionalitas tidak menyediakan daftar file hanya di folder tertentu, melainkan di anak-anaknya?
Dr_Zaszuś

@ Dr_Zaszuś: maaf? **/memberikan daftar nama direktori di direktori kerja saat ini, karena polanya diakhiri /, dan dengan recursive=FalseAnda pada dasarnya memiliki ganda *, yang cocok sama */, hanya kurang efisien.
Martijn Pieters

@ Dr_Zaszuś: gunakan */*jika Anda membutuhkan semua file di semua subdirektori.
Martijn Pieters

22

Untuk menemukan file di subdirektori langsung:

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

Untuk versi rekursif yang melintasi semua subdirektori, Anda dapat menggunakan **dan meneruskan recursive=True sejak Python 3.5 :

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

Kedua panggilan fungsi mengembalikan daftar. Anda bisa menggunakan glob.iglob()untuk mengembalikan jalur satu per satu. Atau gunakanpathlib :

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

Kedua metode mengembalikan iterator (Anda bisa mendapatkan jalur satu per satu).


Ya, saya mengerti itu; tetapi saya juga tidak berharap glob()untuk mendukung pola dalam direktori.
Martijn Pieters

Komentar dihapus, saya lihat sekarang memberikan kesan yang salah; Selain itu, tambalan tersebut menyertakan pembaruan dokumentasi untuk **kasus rekursi. Tetapi untuk **bekerja, Anda harus mengatur recursion=Truesakelar, btw.
Martijn Pieters

20

Ada banyak kebingungan tentang topik ini. Biarkan saya melihat apakah saya dapat menjelaskannya (Python 3.7):

  1. glob.glob('*.txt') :cocok dengan semua file yang diakhiri dengan '.txt' di direktori saat ini
  2. glob.glob('*/*.txt') :sama seperti 1
  3. glob.glob('**/*.txt') :cocok dengan semua file yang diakhiri dengan '.txt' di subdirektori langsung saja , tetapi tidak di direktori saat ini
  4. glob.glob('*.txt',recursive=True) :sama seperti 1
  5. glob.glob('*/*.txt',recursive=True) :sama seperti 3
  6. glob.glob('**/*.txt',recursive=True):cocok dengan semua file yang diakhiri dengan '.txt' di direktori saat ini dan di semua subdirektori

Jadi yang terbaik adalah selalu menentukan recursive=True.


1
Ini harus menjadi jawaban teratas!
Abhik Sarkar

17

The glob2 paket mendukung kartu liar dan cukup cepat

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

Di laptop saya, dibutuhkan sekitar 2 detik untuk mencocokkan > 60.000 jalur file .


9

Anda dapat menggunakan Formic dengan Python 2.6

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

Pengungkapan - Saya adalah pembuat paket ini.


4

Berikut adalah versi adaptasi yang memungkinkan glob.globfungsionalitas serupa tanpa menggunakan 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

Jadi jika Anda memiliki struktur dir berikut

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

Anda bisa melakukan sesuatu seperti ini

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

Cukup banyak fnmatchpola yang cocok di seluruh nama file itu sendiri, bukan hanya nama file.


2

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

Tidak berfungsi untuk semua kasus, gunakan glob2

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

2

Jika Anda dapat menginstal paket glob2 ...

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

Semua nama file dan folder:

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

2

Jika Anda menjalankan Python 3.4+, Anda dapat menggunakan pathlibmodul. The Path.glob()Metode mendukung **pola, yang berarti “direktori ini dan semua subdirektori, rekursif”. Ini mengembalikan generator yang menghasilkan Pathobjek untuk semua file yang cocok.

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

0

Seperti yang ditunjukkan oleh Martijn, glob hanya dapat melakukan ini melalui **operator yang diperkenalkan dengan Python 3.5. Karena OP secara eksplisit meminta modul glob, berikut ini akan mengembalikan iterator evaluasi malas yang berperilaku serupa

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

Perhatikan bahwa Anda hanya dapat mengulang sekali configfilesdalam pendekatan ini. Jika Anda memerlukan daftar sebenarnya dari configfiles yang dapat digunakan dalam beberapa operasi, Anda harus membuatnya secara eksplisit dengan menggunakan list(configfiles).


0

Perintah rglob akan melakukan rekursi tak terbatas ke sub-level terdalam dari struktur direktori Anda. Jika Anda hanya menginginkan satu level dalam, maka jangan gunakan itu.

Saya menyadari OP sedang berbicara tentang penggunaan glob.glob. Saya yakin ini menjawab maksudnya, yaitu untuk mencari semua subfolder secara rekursif.

The rglobfungsi baru-baru ini menghasilkan peningkatan 100x dalam kecepatan untuk algoritma pengolahan data yang menggunakan struktur folder sebagai asumsi tetap untuk urutan membaca data. Namun, dengan rglobkami dapat melakukan satu pemindaian sekali melalui semua file pada atau di bawah direktori induk yang ditentukan, menyimpan nama mereka ke daftar (lebih dari satu juta file), kemudian menggunakan daftar tersebut untuk menentukan file mana yang perlu kami buka kapan saja. titik di masa mendatang hanya berdasarkan konvensi penamaan file vs. folder tempat mereka berada.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.