Vários tipos de arquivos Python glob


142

Existe uma maneira melhor de usar glob.glob em python para obter uma lista de vários tipos de arquivos, como .txt, .mdown e .markdown? Agora eu tenho algo parecido com isto:

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

Respostas:


156

Talvez exista uma maneira melhor, mas que 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

Talvez exista outra maneira, então aguarde, caso alguém encontre uma resposta melhor.


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

10
A solução da Novitoll é curta, mas acaba criando listas aninhadas.
robroc

9
você sempre pode fazer isso;)[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 em [' .pdf', '* .cpp']]
florisla:

3
Isso percorre duas vezes a lista de arquivos. Na primeira iteração, verifica * .pdf e, na segunda, verifica * .cpp. Existe uma maneira de fazer isso em uma iteração? Verifique a condição combinada de cada vez?
Ridhuvarshan #

47
from glob import glob

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

print(files)

Se você precisar especificar um caminho, faça um loop sobre os padrões de correspondência e mantenha a junção dentro do loop 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 retorna uma lista: por que não executá-lo várias vezes e concatenar os resultados?

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

3
Esta é possivelmente a solução mais legível fornecida. Eu mudaria o caso de ProjectFilespara projectFiles, mas ótima solução.
Hans Goldman

40

Encadear os resultados:

import itertools as it, glob

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

Então:

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

13
glob.glob -> glob.iglob de modo que a cadeia iterators é totalmente preguiçoso avaliadas
rodrigob

1
Encontrei a mesma solução, mas não sabia chain.from_iterable. Portanto, este é semelhante, mas menos legível: it.chain(*(glob.iglob(pattern) for pattern in patterns)).
florisla

17

Tantas respostas que sugerem globbing quantas vezes o número de extensões, preferiria globbing apenas uma vez:

from pathlib import Path

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

15

com glob não é possível. você pode usar apenas:
* corresponde a tudo
? corresponde a qualquer caractere único
[seq] corresponde a qualquer caractere em seq
[! seq] corresponde a qualquer caractere que não esteja em seq

use os.listdir e um regexp para verificar padrões:

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

10

1
Eu gosto dessa abordagem - se a expressividade da glob não for suficientemente poderosa, atualize para um sistema regex mais poderoso, não a use usando, por exemplo, itertoolsporque alterações subsequentes nos padrões também precisam ser hacky (digamos que você queira permitir maiúsculas e minúsculas) . Oh, e que poderia ser mais limpo para escrever'.*\.(txt|sql)'
metakermit

Existe alguma razão para preferir os.listdir ('.') Sobre glob.iglob (' . ')?
Mr.WorshipMe

14

Por exemplo, para *.mp3e *.flacem várias pastas, você pode:

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

A idéia pode ser estendida para mais extensões de arquivo, mas você deve verificar se as combinações não correspondem a nenhuma outra extensão de arquivo indesejada que você possa ter nessas pastas. Portanto, tenha cuidado com isso.

Para combinar automaticamente uma lista arbitrária de extensões em um único padrão glob, você pode fazer o seguinte:

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

A one-liner, apenas para o 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]

resultado:

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

4

Depois de vir aqui para obter ajuda, fiz minha própria solução e queria compartilhá-la. É baseado na resposta do usuário2363986, mas acho que isso é mais escalável. Ou seja, se você tiver 1000 extensões, o código ainda parecerá um pouco 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

Não funciona para mim. Eu usodirectoryPath = "/Users/bla/bla/images_dir*."
NeStack

Eu precisaria de mais informações para depurar isso para você ... Você está recebendo uma exceção? Além disso, se você estiver no Windows, esse caminho não parece que funcionaria (letra da unidade ausente).
Hans Goldman

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

4
Boas respostas também fornecem algumas explicações sobre o código e talvez até algumas das suas razões por trás do código.
SunSparc

4

Enquanto o glob padrão do Python não segue realmente o glob do Bash, você pode fazer isso com outras bibliotecas. Podemos ativar chaves no globo do wcmatch .

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

Você pode até usar padrões glob estendidos se essa for sua preferência:

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

Isso não leva a recursivebandeira
Shamoon 24/03

@ Shamoon Não, leva a glob.GLOBSTARbandeira
facelessuser

3

Eu liberei fórmico que múltiplos implementos inclui de um modo semelhante ao do Apache Ant FileSet e Globs .

A pesquisa pode ser implementada:

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

Como o Ant glob completo é implementado, você pode incluir diretórios diferentes em cada padrão, para escolher apenas os arquivos .txt em um subdiretório e o .markdown em outro, por exemplo:

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

Eu espero que isso ajude.


3

A função a seguir _globmostra várias extensões de arquivo.

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 é uma pathlibsolução 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("~"))

Também ignora todos os nomes de arquivos iniciados por ~.


3

Aqui está uma variante de compreensão de lista de uma linha da resposta de Pat (que também inclui o que você queria escrever em um diretório específico do projeto):

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

Você percorre as extensões ( for ext in exts) e, em seguida, para cada extensão, você pega cada arquivo que corresponde ao padrão glob ( for f in glob.glob(os.path.join(project_dir, ext)).

Essa solução é curta e sem nenhum loop for desnecessário, compreensão de lista aninhada ou funções para desorganizar o código. Zen puro, expressivo e pitônico .

Esta solução permite que você tenha uma lista personalizada extsque pode ser alterada sem precisar atualizar seu código. (Essa é sempre uma boa prática!)

A compreensão da lista é a mesma usada na solução de Laurent (na qual votei). Mas eu argumentaria que geralmente não é necessário fatorar uma única linha para uma função separada, e é por isso que estou fornecendo isso como uma solução alternativa.

Bônus:

Se você precisar pesquisar não apenas um único diretório, mas também todos os subdiretórios, poderá passar recursive=Truee usar o símbolo glob de vários diretórios ** 1 :

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

Isso será chamado glob.glob('<project_dir>/**/*.txt', recursive=True)e assim por diante para cada extensão.

1 Tecnicamente, o **símbolo da glob simplesmente corresponde a um ou mais caracteres, incluindo barra / (ao contrário do *símbolo da glob singular ). Na prática, basta lembrar que, desde que você rodeie **com barras (separadores de caminho), ele corresponde a zero ou mais diretórios.


2

Não glob, mas aqui está outra maneira de usar uma compreensão de lista:

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

1

Você pode tentar fazer uma lista manual comparando a extensão existente com as necessárias.

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 globvários tipos de arquivo, você precisa chamar a glob()função várias vezes em um loop. Como essa função retorna uma lista, você precisa concatenar as listas.

Por exemplo, esta função faz o trabalho:

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 simples:

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

Você também pode usar glob.iglob()para ter um iterador:

Retorne um iterador que produz os mesmos valores que glob () sem realmente armazená-los todos simultaneamente.

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

1

Use uma lista de extensão e itere através

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

Você pode usar o 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

Você também pode usar reduce()assim:

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

isso cria uma lista glob.glob()para cada padrão e os reduz para uma única lista.


0

Um globo, muitas extensões ... mas solução imperfeita (pode corresponder a outros arquivos).

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

Eu tive o mesmo problema e é isso que eu criei

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

Outra solução (use globpara obter caminhos usando a correspondência múltipla patternse combinar todos os caminhos em uma única lista usando reducee add):

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

0

Se você usar, pathlibtente o seguinte:

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

Pelos resultados que obtive de testes empíricos, descobriu-se que essa glob.globnão é a melhor maneira de filtrar arquivos por suas extensões. Alguns dos motivos são:

  • A " linguagem " globbing não permite a especificação perfeita de múltiplas extensões.
  • O primeiro ponto resulta na obtenção de resultados incorretos, dependendo das extensões do arquivo.
  • O método de globbing é empiricamente comprovado como mais lento do que a maioria dos outros métodos.
  • Mesmo que seja estranho, mesmo outros objetos do sistema de arquivos podem ter " extensões ", pastas também.

Testei (para correção e eficiência no tempo) os seguintes 4métodos diferentes para filtrar arquivos por extensões e colocá-los em 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

Ao executar o código acima no meu laptop, obtive os seguintes resultados auto-explicativos.

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.

A maneira mais rápida de filtrar arquivos por extensões, é até a mais feia. Ou seja, forloops aninhados e stringcomparação usando o endswith()método

Além disso, como você pode ver, os algoritmos de globbing (com o padrão E:\x\y\z\**/*[py][pyc]), mesmo com apenas a 2extensão fornecida ( pye pyc), também retornam resultados incorretos.


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)

Oi Sway Wu, seja bem-vindo. Por favor, considere adicionar uma explicação.
Tiago Martins Peres

-1

Isso deve funcionar:

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

-1

Por exemplo:

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

Uma função:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.