Python glob più tipi di file


142

Esiste un modo migliore per utilizzare glob.glob in Python per ottenere un elenco di più tipi di file come .txt, .mdown e .markdown? In questo momento ho qualcosa del genere:

projectFiles1 = glob.glob( os.path.join(projectDir, '*.txt') )
projectFiles2 = glob.glob( os.path.join(projectDir, '*.mdown') )
projectFiles3 = glob.glob( os.path.join(projectDir, '*.markdown') )

Risposte:


156

Forse c'è un modo migliore, ma che ne dici di:

import glob
types = ('*.pdf', '*.cpp') # the tuple of file types
files_grabbed = []
for files in types:
    files_grabbed.extend(glob.glob(files))

# files_grabbed is the list of pdf and cpp files

Forse c'è un altro modo, quindi aspetta nel caso in cui qualcun altro trovi una risposta migliore.


19
files_grabbed = [glob.glob(e) for e in ['*.pdf', '*.cpp']]
Novitoll,

10
La soluzione di Novitoll è breve, ma finisce per creare elenchi nidificati.
robroc,

9
potresti sempre farlo;)[f for f_ in [glob.glob(e) for e in ('*.jpg', '*.mp4')] for f in f_]
AlexG

1
files_grabbed = [ glob.glob (e) per e in [' .pdf', '* .cpp']]
florisla,

3
Questo scorre due volte nell'elenco dei file. Nella prima iterazione verifica la presenza di * .pdf e nella seconda verifica la presenza di * .cpp. C'è un modo per farlo in una iterazione? Controlla le condizioni combinate ogni volta?
Ridhuvarshan,

47
from glob import glob

files = glob('*.gif')
files.extend(glob('*.png'))
files.extend(glob('*.jpg'))

print(files)

Se è necessario specificare un percorso, scorrere sopra i motivi di corrispondenza e mantenere l'unione all'interno del ciclo per semplicità:

from os.path import join
from glob import glob

files = []
for ext in ('*.gif', '*.png', '*.jpg'):
   files.extend(glob(join("path/to/dir", ext)))

print(files)

45

glob restituisce un elenco: perché non eseguirlo più volte e concatenare i risultati?

from glob import glob
project_files = glob('*.txt') + glob('*.mdown') + glob('*.markdown')

3
Questa è probabilmente la soluzione più leggibile fornita. Vorrei cambiare il caso di ProjectFilesa projectFiles, ma grande soluzione.
Hans Goldman,

40

Incatenare i risultati:

import itertools as it, glob

def multiple_file_types(*patterns):
    return it.chain.from_iterable(glob.iglob(pattern) for pattern in patterns)

Poi:

for filename in multiple_file_types("*.txt", "*.sql", "*.log"):
    # do stuff

13
glob.glob -> glob.iglob in modo che la catena di iteratori sia completamente pigra valutata
rodrigob

1
Ho trovato la stessa soluzione ma non lo sapevo chain.from_iterable. Quindi questo è simile, ma meno leggibile: it.chain(*(glob.iglob(pattern) for pattern in patterns)).
florisla,

17

Così tante risposte che suggeriscono il globbing tante volte quante sono le estensioni, preferirei invece il globbing solo una volta:

from pathlib import Path

files = {p.resolve() for p in Path(path).glob("**/*") if p.suffix in [".c", ".cc", ".cpp", ".hxx", ".h"]}

15

con glob non è possibile. puoi usare solo:
* corrisponde a tutto
? corrisponde a qualsiasi carattere singolo
[seq] corrisponde a qualsiasi carattere in seq
[! seq] corrisponde a qualsiasi carattere non in seq

usa os.listdir e un regexp per controllare i pattern:

for x in os.listdir('.'):
  if re.match('.*\.txt|.*\.sql', x):
    print x

10
termina la tua regex con $ in modo che corrisponda solo alla fine dei nomi dei file
ThiefMaster

1
Mi piace questo approccio: se l'espressività di glob non è abbastanza potente, passa a un sistema regex più potente, non hackerarlo usando ad esempio itertoolsperché anche i successivi cambi di pattern devono essere caotici (supponiamo che tu voglia consentire maiuscole e minuscole) . Oh, e potrebbe essere più pulito scrivere'.*\.(txt|sql)'
metakermit

C'è qualche motivo per preferire os.listdir ('.') Su glob.iglob (' . ')?
Mr.Worship,

14

Ad esempio, per *.mp3e *.flacsu più cartelle, puoi fare:

mask = r'music/*/*.[mf][pl][3a]*'
glob.glob(mask)

L'idea può essere estesa a più estensioni di file, ma è necessario verificare che le combinazioni non corrispondano a qualsiasi altra estensione di file indesiderata che è possibile avere su quelle cartelle. Quindi, stai attento con questo.

Per combinare automaticamente un elenco arbitrario di estensioni in un unico modello glob, è possibile effettuare le seguenti operazioni:

mask_base = r'music/*/*.'
exts = ['mp3', 'flac', 'wma']
chars = ''.join('[{}]'.format(''.join(set(c))) for c in zip(*exts))
mask = mask_base + chars + ('*' if len(set(len(e) for e in exts)) > 1 else '')
print(mask)  # music/*/*.[fmw][plm][3a]*

6

Un one-liner, solo per l'inferno ..

folder = "C:\\multi_pattern_glob_one_liner"
files = [item for sublist in [glob.glob(folder + ext) for ext in ["/*.txt", "/*.bat"]] for item in sublist]

produzione:

['C:\\multi_pattern_glob_one_liner\\dummy_txt.txt', 'C:\\multi_pattern_glob_one_liner\\dummy_bat.bat']

4

Dopo essere venuto qui per chiedere aiuto, ho creato la mia soluzione e volevo condividerla. Si basa sulla risposta di user2363986, ma penso che sia più scalabile. Significa che se hai 1000 estensioni, il codice apparirà comunque piuttosto elegante.

from glob import glob

directoryPath  = "C:\\temp\\*." 
fileExtensions = [ "jpg", "jpeg", "png", "bmp", "gif" ]
listOfFiles    = []

for extension in fileExtensions:
    listOfFiles.extend( glob( directoryPath + extension ))

for file in listOfFiles:
    print(file)   # Or do other stuff

Non funziona per me. Io usodirectoryPath = "/Users/bla/bla/images_dir*."
NeStack il

Avrei bisogno di maggiori informazioni per eseguire il debug di questo per te ... Stai ricevendo un'eccezione? Inoltre, se sei su Windows, quel percorso non sembra funzionare (lettera di unità mancante).
Hans Goldman,

4
files = glob.glob('*.txt')
files.extend(glob.glob('*.dat'))

4
Le buone risposte forniscono anche una spiegazione del codice e forse anche alcuni dei tuoi ragionamenti alla base del codice.
SunSparc,

4

Mentre il glob predefinito di Python non segue realmente il glob di Bash, puoi farlo con altre librerie. Possiamo abilitare le parentesi graffe nel glob di wcmatch .

>>> from wcmatch import glob
>>> glob.glob('*.{md,ini}', flags=glob.BRACE)
['LICENSE.md', 'README.md', 'tox.ini']

Puoi anche usare modelli glob estesi se questa è la tua preferenza:

from wcmatch import glob
>>> glob.glob('*.@(md|ini)', flags=glob.EXTGLOB)
['LICENSE.md', 'README.md', 'tox.ini']

Questo non porta la recursivebandiera
Shamoon

@Shamoon No, ci vuole la glob.GLOBSTARbandiera
facelessuser

3

Ho rilasciato Formic che implementa molteplici include in modo simile al FileSet e Globs di Apache Ant .

La ricerca può essere implementata:

import formic
patterns = ["*.txt", "*.markdown", "*.mdown"]
fileset = formic.FileSet(directory=projectDir, include=patterns)
for file_name in fileset.qualified_files():
    # Do something with file_name

Poiché è implementato l'intero Ant glob, è possibile includere diverse directory con ogni modello, quindi è possibile scegliere solo quei file .txt in una sottodirectory e il .markdown in un'altra, ad esempio:

patterns = [ "/unformatted/**/*.txt", "/formatted/**/*.mdown" ]

Spero che aiuti.


3

Le seguenti funzioni _globglobs per più estensioni di file.

import glob
import os
def _glob(path, *exts):
    """Glob for multiple file extensions

    Parameters
    ----------
    path : str
        A file name without extension, or directory name
    exts : tuple
        File extensions to glob for

    Returns
    -------
    files : list
        list of files matching extensions in exts in path

    """
    path = os.path.join(path, "*") if os.path.isdir(path) else path + "*"
    return [f for files in [glob.glob(path + ext) for ext in exts] for f in files]

files = _glob(projectDir, ".txt", ".mdown", ".markdown")

3

Questa è una pathlibsoluzione Python 3.4+ :

exts = ".pdf", ".doc", ".xls", ".csv", ".ppt"
filelist = (str(i) for i in map(pathlib.Path, os.listdir(src)) if i.suffix.lower() in exts and not i.stem.startswith("~"))

Inoltre ignora tutti i nomi di file che iniziano con ~.


3

Ecco una variante di una riga di comprensione dell'elenco della risposta di Pat (che include anche che si voleva glob in una specifica directory di progetto):

import os, glob
exts = ['*.txt', '*.mdown', '*.markdown']
files = [f for ext in exts for f in glob.glob(os.path.join(project_dir, ext))]

Passa il ciclo sopra le estensioni ( for ext in exts), quindi per ogni estensione prendi ciascun file corrispondente al modello glob ( for f in glob.glob(os.path.join(project_dir, ext)).

Questa soluzione è breve e senza inutili loop for, comprensioni di elenchi nidificati o funzioni per ingombrare il codice. Solo puro, espressivo, zen pitone .

Questa soluzione ti consente di avere un elenco personalizzato di extsciò che può essere modificato senza dover aggiornare il tuo codice. (Questa è sempre una buona pratica!)

La comprensione della lista è la stessa usata nella soluzione di Laurent (per la quale ho votato). Ma direi che di solito non è necessario scomporre una singola riga in una funzione separata, motivo per cui sto fornendo questo come soluzione alternativa.

Bonus:

Se devi cercare non solo una singola directory, ma anche tutte le sottodirectory, puoi passare recursive=Truee utilizzare il simbolo glob multi-directory ** 1 :

files = [f for ext in exts 
         for f in glob.glob(os.path.join(project_dir, '**', ext), recursive=True)]

Questo invocherà glob.glob('<project_dir>/**/*.txt', recursive=True)e così via per ogni estensione.

1 Tecnicamente, il **simbolo glob corrisponde semplicemente a uno o più caratteri inclusa la barra / (diversamente dal singolare *simbolo glob). In pratica, devi solo ricordare che fintanto che circondi **di barre (separatori di percorso), corrisponde a zero o più directory.


2

No glob, ma ecco un altro modo usando una comprensione dell'elenco:

extensions = 'txt mdown markdown'.split()
projectFiles = [f for f in os.listdir(projectDir) 
                  if os.path.splitext(f)[1][1:] in extensions]

1

Puoi provare a creare un elenco manuale confrontando l'estensione di quelli esistenti con quelli richiesti.

ext_list = ['gif','jpg','jpeg','png'];
file_list = []
for file in glob.glob('*.*'):
  if file.rsplit('.',1)[1] in ext_list :
    file_list.append(file)


1

Per globpiù tipi di file, è necessario chiamare la glob()funzione più volte in un ciclo. Poiché questa funzione restituisce un elenco, è necessario concatenare gli elenchi.

Ad esempio, questa funzione svolge il compito:

import glob
import os


def glob_filetypes(root_dir, *patterns):
    return [path
            for pattern in patterns
            for path in glob.glob(os.path.join(root_dir, pattern))]

Semplice utilizzo:

project_dir = "path/to/project/dir"
for path in sorted(glob_filetypes(project_dir, '*.txt', '*.mdown', '*.markdown')):
    print(path)

Puoi anche usare glob.iglob()un iteratore:

Restituisce un iteratore che produce gli stessi valori di glob () senza effettivamente memorizzarli tutti contemporaneamente.

def iglob_filetypes(root_dir, *patterns):
    return (path
            for pattern in patterns
            for path in glob.iglob(os.path.join(root_dir, pattern)))

1

Utilizzare un elenco di estensioni e scorrere

from os.path import join
from glob import glob

files = []
extensions = ['*.gif', '*.png', '*.jpg']
for ext in extensions:
   files.extend(glob(join("path/to/dir", ext)))

print(files)

0

Puoi usare il filtro:

import os
import glob

projectFiles = filter(
    lambda x: os.path.splitext(x)[1] in [".txt", ".mdown", ".markdown"]
    glob.glob(os.path.join(projectDir, "*"))
)

0

Puoi anche usare reduce()così:

import glob
file_types = ['*.txt', '*.mdown', '*.markdown']
project_files = reduce(lambda list1, list2: list1 + list2, (glob.glob(t) for t in file_types))

questo crea un elenco glob.glob()per ogni modello e li riduce in un unico elenco.


0

Un glob, molte estensioni ... ma soluzione imperfetta (potrebbe corrispondere ad altri file).

filetypes = ['tif', 'jpg']

filetypes = zip(*[list(ft) for ft in filetypes])
filetypes = ["".join(ch) for ch in filetypes]
filetypes = ["[%s]" % ch for ch in filetypes]
filetypes = "".join(filetypes) + "*"
print(filetypes)
# => [tj][ip][fg]*

glob.glob("/path/to/*.%s" % filetypes)

0

Ho avuto lo stesso problema e questo è quello che mi è venuto in mente

import os, sys, re

#without glob

src_dir = '/mnt/mypics/'
src_pics = []
ext = re.compile('.*\.(|{}|)$'.format('|'.join(['png', 'jpeg', 'jpg']).encode('utf-8')))
for root, dirnames, filenames in os.walk(src_dir):
  for filename in filter(lambda name:ext.search(name),filenames):
    src_pics.append(os.path.join(root, filename))

0

Ancora un'altra soluzione (usare globper ottenere percorsi usando più corrispondenze patternse combinare tutti i percorsi in un unico elenco usando reducee add):

import functools, glob, operator
paths = functools.reduce(operator.add, [glob.glob(pattern) for pattern in [
    "path1/*.ext1",
    "path2/*.ext2"]])

0

Se usi pathlibprova questo:

import pathlib

extensions = ['.py', '.txt']
root_dir = './test/'

files = filter(lambda p: p.suffix in extensions, pathlib.Path(root_dir).glob('**/*'))

print(list(files))

0

Dai risultati ottenuti dai test empirici, si è scoperto che glob.globnon è il modo migliore per filtrare i file in base alle loro estensioni. Alcuni dei motivi sono:

  • Il " linguaggio " globbing non consente la specifica perfetta di estensione multipla.
  • Nel primo punto si ottengono risultati errati a seconda delle estensioni dei file.
  • Il metodo globbing è empiricamente dimostrato di essere più lento della maggior parte degli altri metodi.
  • Anche se è strano, anche gli oggetti di altri filesystem possono avere " estensioni ", anche le cartelle.

Ho testato (per correttezza ed efficienza nel tempo) i seguenti 4diversi metodi per filtrare i file per estensione e inserirli in un list:

from glob import glob, iglob
from re import compile, findall
from os import walk


def glob_with_storage(args):

    elements = ''.join([f'[{i}]' for i in args.extensions])
    globs = f'{args.target}/**/*{elements}'
    results = glob(globs, recursive=True)

    return results


def glob_with_iteration(args):

    elements = ''.join([f'[{i}]' for i in args.extensions])
    globs = f'{args.target}/**/*{elements}'
    results = [i for i in iglob(globs, recursive=True)]

    return results


def walk_with_suffixes(args):

    results = []
    for r, d, f in walk(args.target):
        for ff in f:
            for e in args.extensions:
                if ff.endswith(e):
                    results.append(path_join(r,ff))
                    break
    return results


def walk_with_regs(args):

    reg = compile('|'.join([f'{i}$' for i in args.extensions]))

    results = []
    for r, d, f in walk(args.target):
        for ff in f:
            if len(findall(reg,ff)):
                results.append(path_join(r, ff))

    return results

Eseguendo il codice sopra sul mio laptop ho ottenuto i seguenti risultati autoesplicativi.

Elapsed time for '7 times glob_with_storage()':  0.365023 seconds.
mean   : 0.05214614
median : 0.051861
stdev  : 0.001492152
min    : 0.050864
max    : 0.054853

Elapsed time for '7 times glob_with_iteration()':  0.360037 seconds.
mean   : 0.05143386
median : 0.050864
stdev  : 0.0007847381
min    : 0.050864
max    : 0.052859

Elapsed time for '7 times walk_with_suffixes()':  0.26529 seconds.
mean   : 0.03789857
median : 0.037899
stdev  : 0.0005759071
min    : 0.036901
max    : 0.038896

Elapsed time for '7 times walk_with_regs()':  0.290223 seconds.
mean   : 0.04146043
median : 0.040891
stdev  : 0.0007846776
min    : 0.04089
max    : 0.042885

Results sizes:
0 2451
1 2451
2 2446
3 2446

Differences between glob() and walk():
0 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Includes\numpy
1 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Utility\CppSupport.cpp
2 E:\x\y\z\venv\lib\python3.7\site-packages\future\moves\xmlrpc
3 E:\x\y\z\venv\lib\python3.7\site-packages\Cython\Includes\libcpp
4 E:\x\y\z\venv\lib\python3.7\site-packages\future\backports\xmlrpc

Elapsed time for 'main':  1.317424 seconds.

Il modo più veloce per filtrare i file in base alle estensioni, sembra addirittura essere il più brutto. Cioè, forloop nidificati e stringconfronto utilizzando il endswith()metodo.

Inoltre, come puoi vedere, gli algoritmi globbing (con il modello E:\x\y\z\**/*[py][pyc]) anche con solo l' 2estensione data ( pye pyc) restituiscono anche risultati errati.


0
import glob
import pandas as pd

df1 = pd.DataFrame(columns=['A'])
for i in glob.glob('C:\dir\path\*.txt'):
    df1 = df1.append({'A': i}, ignore_index=True)
for i in glob.glob('C:\dir\path\*.mdown'):
    df1 = df1.append({'A': i}, ignore_index=True)
for i in glob.glob('C:\dir\path\*.markdown):
    df1 = df1.append({'A': i}, ignore_index=True)

Ciao Sway Wu, benvenuto. Si prega di considerare l'aggiunta di una spiegazione.
Tiago Martins Peres

-1

Questo dovrebbe funzionare:

import glob
extensions = ('*.txt', '*.mdown', '*.markdown')
for i in extensions:
    for files in glob.glob(i):
        print (files)

-1

Per esempio:

import glob
lst_img = []
base_dir = '/home/xy/img/'

# get all the jpg file in base_dir 
lst_img += glob.glob(base_dir + '*.jpg')
print lst_img
# ['/home/xy/img/2.jpg', '/home/xy/img/1.jpg']

# append all the png file in base_dir to lst_img
lst_img += glob.glob(base_dir + '*.png')
print lst_img
# ['/home/xy/img/2.jpg', '/home/xy/img/1.jpg', '/home/xy/img/3.png']

Una funzione:

import glob
def get_files(base_dir='/home/xy/img/', lst_extension=['*.jpg', '*.png']):
    """
    :param base_dir:base directory
    :param lst_extension:lst_extension: list like ['*.jpg', '*.png', ...]
    :return:file lists like ['/home/xy/img/2.jpg','/home/xy/img/3.png']
    """
    lst_files = []
    for ext in lst_extension:
        lst_files += glob.glob(base_dir+ext)
    return lst_files
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.