Python glob múltiples tipos de archivos


142

¿Hay una mejor manera de usar glob.glob en python para obtener una lista de múltiples tipos de archivos como .txt, .mdown y .markdown? En este momento tengo algo como esto:

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

Respuestas:


156

Tal vez hay una mejor manera, pero ¿qué tal:

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

Quizás haya otra manera, así que espere en caso de que alguien más presente una mejor respuesta.


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

10
La solución de Novitoll es corta, pero termina creando listas anidadas.
robroc

9
siempre puedes hacer esto;)[f for f_ in [glob.glob(e) for e in ('*.jpg', '*.mp4')] for f in f_]
AlexG

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

3
Esto recorre dos veces la lista de archivos. En la primera iteración, busca * .pdf y en la segunda, busca * .cpp. ¿Hay alguna manera de hacerlo en una iteración? ¿Verifica la condición combinada cada vez?
Ridhuvarshan

47
from glob import glob

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

print(files)

Si necesita especificar una ruta, repita los patrones de coincidencia y mantenga la unión dentro del bucle para simplificar:

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 devuelve una lista: ¿por qué no simplemente ejecutarla varias veces y concatenar los resultados?

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

3
Esta es posiblemente la solución más fácil de leer. Cambiaría el caso de ProjectFilesa projectFiles, pero es una gran solución.
Hans Goldman

40

Encadena los resultados:

import itertools as it, glob

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

Luego:

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

13
glob.glob -> glob.iglob para que la cadena de iteradores sea completamente floja evaluada
rodrigob

1
Encontré la misma solución pero no lo sabía chain.from_iterable. Así que esto es similar, pero menos legible: it.chain(*(glob.iglob(pattern) for pattern in patterns)).
florisla

17

Tantas respuestas que sugieren englobar tantas veces como el número de extensiones, preferiría hacerlo solo una vez:

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 no es posible. solo puedes usar:
* ¿coincide con todo
? coincide con cualquier carácter individual
[seq] coincide con cualquier carácter en seq
[! seq] coincide con cualquier carácter que no esté en seq

use os.listdir y una expresión regular para verificar patrones:

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

10
finalice su expresión regular con $ para que coincida solo con el final de los nombres de archivo
ThiefMaster

1
Me gusta este enfoque: si la expresividad de Glob no es lo suficientemente potente, actualice a un sistema de expresiones regulares más potente, no lo piratee utilizando, por ejemplo, itertoolsporque los cambios de patrón posteriores también deben ser extravagantes (digamos que desea permitir mayúsculas y minúsculas) . Ah, y podría ser más limpio escribir'.*\.(txt|sql)'
metakermit

¿Hay alguna razón para preferir os.listdir ('.') Sobre glob.iglob (' . ')?
Mr.WorshipMe

14

Por ejemplo, para *.mp3y *.flacen varias carpetas, puede hacer:

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

La idea puede extenderse a más extensiones de archivo, pero debe verificar que las combinaciones no coincidan con ninguna otra extensión de archivo no deseada que pueda tener en esas carpetas. Entonces ten cuidado con esto.

Para combinar automáticamente una lista arbitraria de extensiones en un único patrón global, puede hacer lo siguiente:

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

Una frase, solo por el placer de hacerlo ...

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]

salida:

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

4

Después de venir aquí por ayuda, hice mi propia solución y quería compartirla. Se basa en la respuesta de user2363986, pero creo que esto es más escalable. Lo que significa que si tiene 1000 extensiones, el código seguirá luciendo 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

No funciona para mi Yo usodirectoryPath = "/Users/bla/bla/images_dir*."
NeStack

Necesitaría más información para depurar esto para usted ... ¿Está recibiendo una excepción? Además, si está en Windows, esa ruta no parece funcionar (falta letra de unidad).
Hans Goldman el

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

44
Las buenas respuestas también proporcionan alguna explicación del código y quizás incluso algunos de sus razonamientos detrás del código.
SunSparc

4

Si bien el glob predeterminado de Python realmente no sigue al glob de Bash, puede hacerlo con otras bibliotecas. Podemos habilitar llaves en el globo de wcmatch .

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

Incluso puede usar patrones de glob extendidos si esa es su preferencia:

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

Esto no toma la recursivebandera
Shamoon

@Shamoon No, toma la glob.GLOBSTARbandera
sin rostro

3

He lanzado Formic, que implementa múltiples inclusiones de manera similar a FileSet y Globs de Apache Ant .

La búsqueda se puede implementar:

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

Debido a que se implementa el glob Ant completo, puede incluir diferentes directorios con cada patrón, por lo que puede elegir solo esos archivos .txt en un subdirectorio y el .markdown en otro, por ejemplo:

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

Espero que esto ayude.


3

La siguiente función _globpegotes de varias extensiones de archivo.

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

Esta es una pathlibsolución 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("~"))

También ignora todos los nombres de archivo que comienzan con ~.


3

Aquí hay una variante de comprensión de la lista de una línea de la respuesta de Pat (que también incluye que usted desea glob en un directorio de proyecto específico):

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

Recorre las extensiones ( for ext in exts) y luego, para cada extensión, toma cada archivo que coincida con el patrón global ( for f in glob.glob(os.path.join(project_dir, ext)).

Esta solución es breve y sin ningún for-loop innecesario, comprensión de listas anidadas o funciones para saturar el código. Solo Zen puro, expresivo y pitónico .

Esta solución le permite tener una lista personalizada extsque se puede cambiar sin tener que actualizar su código. (¡Esto siempre es una buena práctica!)

La lista de comprensión es la misma utilizada en la solución de Laurent (por la que he votado). Pero yo diría que generalmente no es necesario factorizar una sola línea a una función separada, por lo que proporciono esto como una solución alternativa.

Prima:

Si necesita buscar no solo en un solo directorio, sino también en todos los subdirectorios, puede pasar recursive=Truey usar el símbolo de globo multidireccional ** 1 :

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

Esto invocará glob.glob('<project_dir>/**/*.txt', recursive=True) y así sucesivamente para cada extensión.

1 Técnicamente, el **símbolo glob simplemente coincide con uno o más caracteres, incluida la barra diagonal / (a diferencia del *símbolo glob singular ). En la práctica, solo debe recordar que, siempre que rodee **con barras diagonales (separadores de ruta), coincide con cero o más directorios.


2

No glob, pero aquí hay otra forma de usar una lista de comprensión:

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

1

Puede intentar hacer una lista manual que compare la extensión de las existentes con las que necesita.

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

Para globvarios tipos de archivos, debe llamarglob() función varias veces en un bucle. Como esta función devuelve una lista, debe concatenar las listas.

Por ejemplo, esta función hace el trabajo:

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

Uso simple:

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

También puede usar glob.iglob()para tener un iterador:

Devuelve un iterador que produce los mismos valores que glob () sin almacenarlos todos simultáneamente.

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

1

Usa una lista de extensiones e itera

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

Podrías usar filter:

import os
import glob

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

0

También puedes usar reduce()así:

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

esto crea una lista glob.glob()para cada patrón y los reduce a una sola lista.


0

Un globo, muchas extensiones ... pero solución imperfecta (podría coincidir con otros archivos).

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

Tuve el mismo problema y esto es lo que se me ocurrió

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

Otra solución más (se usa globpara obtener rutas usando múltiples coincidencias patternsy combinar todas las rutas en una sola lista usando reducey add):

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

0

Si usa pathlibintente esto:

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

Por los resultados que obtuve de las pruebas empíricas, resultó que esa glob.globno es la mejor manera de filtrar archivos por sus extensiones. Algunas de las razones son:

  • El "globbing idioma " no permite la especificación perfecta de múltiples extensiones.
  • El primer punto da como resultado la obtención de resultados incorrectos dependiendo de las extensiones de archivo.
  • El método globbing está empíricamente demostrado que es más lento que la mayoría de los otros métodos.
  • Incluso si es extraño, incluso otros objetos de sistemas de archivos pueden tener " extensiones ", carpetas también.

He probado (para la corrección y la eficiencia en el tiempo) los siguientes 4métodos diferentes para filtrar archivos por extensiones y los coloca en 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

Al ejecutar el código anterior en mi computadora portátil, obtuve los siguientes resultados autoexplicativos.

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.

La forma más rápida de filtrar archivos por extensiones, incluso es la más fea. Es decir, forbucles anidados y stringcomparación usando elendswith() método.

Además, como puede ver, los algoritmos globales (con el patrón E:\x\y\z\**/*[py][pyc]) incluso con la 2extensión dada ( pyy pyc) también devuelve resultados incorrectos.


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)

Hola Sway Wu, bienvenido. Por favor considere agregar una explicación.
Tiago Martins Peres 李大仁

-1

Esto debería funcionar:

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

-1

Por ejemplo:

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 función:

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.