Obter uma lista filtrada de arquivos em um diretório


281

Estou tentando obter uma lista de arquivos em um diretório usando Python, mas não quero uma lista de TODOS os arquivos.

O que eu quero essencialmente é a capacidade de fazer algo como o seguinte, mas usando Python e não executando ls.

ls 145592*.jpg

Se não houver um método interno para isso, estou pensando em escrever um loop for para iterar os resultados de um os.listdir()e anexar todos os arquivos correspondentes a uma nova lista.

No entanto, existem muitos arquivos nesse diretório e, portanto, espero que exista um método mais eficiente (ou um método interno).


[Este link pode ajudá-lo :) Obtenha uma lista filtrada de arquivos em um diretório] ( codereview.stackexchange.com/a/33642 )
sha111 17-19

Observe que você pode ter um cuidado especial com a ordem de classificação, se isso for importante para o seu aplicativo.
Lumbric

Respostas:


385

21
Ah, acabei de notar que os documentos do Python dizem que glob () "é feito usando as funções os.listdir () e fnmatch.fnmatch () em conjunto, e não invocando um subshell". Em outras palavras, glob () não possui as melhorias de eficiência que se poderia esperar.
Ben Hoyt

5
Há uma diferença principal: glob.glob('145592*.jpg')imprime todo o caminho absoluto dos arquivos, enquanto ls 145592*.jpgimprime apenas a lista de arquivos.
EBE Isaac

8
@ Ben Por que invocar um subshell (subprocesso) tem alguma melhoria de eficiência?
Paulo Neves

7
@PauloNeves: verdade, meu comentário acima também não faz sentido para mim 7 anos depois. :-) Acho que estava me referindo ao fato de que glob()apenas usa listdir + fnmatch, em vez de chamadas especiais do sistema operacional para fazer a filtragem de caracteres curinga. Por exemplo, no Windows, a FindFirstFileAPI permite que você especifique curingas para que o sistema operacional faça a filtragem diretamente e, presumivelmente, com mais eficiência (não acho que exista um equivalente no Linux).
Ben Hoyt

1
@ marsh: como sempre, o diretório de trabalho atual do processo.
Ignacio Vazquez-Abrams

124

glob.glob()é definitivamente a maneira de fazê-lo (conforme Ignacio). No entanto, se você precisar de uma correspondência mais complicada, poderá fazê-lo com uma compreensão da lista e re.match(), algo como:

files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]

Mais flexível, mas como você observa, menos eficiente.


Definitivamente, isso parece ser mais poderoso. Por exemplo, ter que fazer algo como[0-9]+
demongolem 10/01

3
Sim, definitivamente mais poderoso - no entanto, o fnmatch suporta [0123456789]sequências ( consulte a documentação ) e também possui a fnmatch.filter()função que torna esse loop um pouco mais eficiente.
precisa

49

Mantenha simples:

import os
relevant_path = "[path to folder]"
included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
file_names = [fn for fn in os.listdir(relevant_path)
              if any(fn.endswith(ext) for ext in included_extensions)]

Eu prefiro essa forma de compreensão de lista porque ela lê bem em inglês.

Li a quarta linha como: Para cada fn no os.listdir do meu caminho, forneça apenas os que correspondem a qualquer uma das minhas extensões incluídas.

Pode ser difícil para os programadores iniciantes de python realmente se acostumarem a usar compreensões de lista para filtragem, e pode ter alguma sobrecarga de memória para conjuntos de dados muito grandes, mas para listar um diretório e outras tarefas simples de filtragem de strings, as compreensões de lista levam a uma limpeza mais clara código documentável.

A única coisa sobre esse design é que ele não protege contra o erro de passar uma string em vez de uma lista. Por exemplo, se você acidentalmente converter uma string em uma lista e acabar verificando todos os caracteres de uma string, poderá acabar recebendo uma série de falsos positivos.

Mas é melhor ter um problema fácil de corrigir do que uma solução difícil de entender.


5
Não que haja necessidade any()aqui, porque str.endswith()leva uma sequência de finais.if fn.endswith(included_extentensions)é mais que suficiente.
Martijn Pieters

3
Além da ineficiência de não usar o str.endswith(seq)que Martijn apontou, isso não está correto, porque um arquivo precisa terminar .extpara ter essa extensão. Este código também encontrará (por exemplo) um arquivo chamado "myjpg" ou um diretório chamado apenas "png". Para corrigir, basta prefixar cada extensão included_extensionscom a ..
Ben Hoyt

Eu sempre sou um pouco cauteloso com o código nas respostas que obviamente não foram executadas ou não podem ser executadas. A variável included_extensionsvs included_extentsions? Uma pena, porque caso contrário, esta é a minha resposta preferida.
Auspice


17

Filtrar com globmódulo:

Import glob

import glob

Curingas:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

Extensão Fiter .txt:

files = glob.glob("/home/ach/*/*.txt")

Um único caractere

glob.glob("/home/ach/file?.txt")

Intervalos numéricos

glob.glob("/home/ach/*[0-9]*")

Faixas do alfabeto

glob.glob("/home/ach/[a-c]*")

12

Código preliminar

import glob
import fnmatch
import pathlib
import os

pattern = '*.py'
path = '.'

Solução 1 - use "glob"

# lookup in current dir
glob.glob(pattern)

In [2]: glob.glob(pattern)
Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']

Solução 2 - use "os" + "fnmatch"

Variante 2.1 - Pesquisa no diretório atual

# lookup in current dir
fnmatch.filter(os.listdir(path), pattern)

In [3]: fnmatch.filter(os.listdir(path), pattern)
Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']

Variante 2.2 - Pesquisa recursiva

# lookup recursive
for dirpath, dirnames, filenames in os.walk(path):

    if not filenames:
        continue

    pythonic_files = fnmatch.filter(filenames, pattern)
    if pythonic_files:
        for file in pythonic_files:
            print('{}/{}'.format(dirpath, file))

Resultado

./wsgi.py
./manage.py
./tasks.py
./temp/temp.py
./apps/diaries/urls.py
./apps/diaries/signals.py
./apps/diaries/actions.py
./apps/diaries/querysets.py
./apps/library/tests/test_forms.py
./apps/library/migrations/0001_initial.py
./apps/polls/views.py
./apps/polls/formsets.py
./apps/polls/reports.py
./apps/polls/admin.py

Solução 3 - use "pathlib"

# lookup in current dir
path_ = pathlib.Path('.')
tuple(path_.glob(pattern))

# lookup recursive
tuple(path_.rglob(pattern))

Notas:

  1. Testado no Python 3.4
  2. O módulo "pathlib" foi adicionado apenas no Python 3.4
  3. O Python 3.5 adicionou um recurso para pesquisa recursiva com glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Como minha máquina está instalada com o Python 3.4, não testei isso.

9

use os.walk para listar recursivamente seus arquivos

import os
root = "/home"
pattern = "145992"
alist_filter = ['jpg','bmp','png','gif'] 
path=os.path.join(root,"mydir_to_scan")
for r,d,f in os.walk(path):
    for file in f:
        if file[-3:] in alist_filter and pattern in file:
            print os.path.join(root,file)

Não há necessidade de cortar; file.endswith(alist_filter)basta.
Martijn Pieters

5
import os

dir="/path/to/dir"
[x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]

Isso fornecerá uma lista de arquivos jpg com o caminho completo. Você pode substituir x[0]+"/"+fcom fpor apenas nomes de arquivos. Você também pode substituir f.endswith(".jpg")por qualquer condição de string que desejar.


3

você também pode gostar de uma abordagem de alto nível (eu implementei e empacotei como findtools ):

from findtools.find_files import (find_files, Match)


# Recursively find all *.txt files in **/home/**
txt_files_pattern = Match(filetype='f', name='*.txt')
found_files = find_files(path='/home', match=txt_files_pattern)

for found_file in found_files:
    print found_file

pode ser instalado com

pip install findtools

2

Nomes de arquivos com extensões "jpg" e "png" em "caminho / para / imagens":

import os
accepted_extensions = ["jpg", "png"]
filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]

Isso é muito parecido com a resposta dada por @ ramsey0
chb

1

Você pode usar o pathlib que está disponível na biblioteca padrão Python 3.4 e posterior.

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

1

Você pode definir um padrão e verificar se há. Aqui eu peguei o padrão inicial e final e procurei no nome do arquivo. ARQUIVOS contém a lista de todos os arquivos em um diretório.

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE and PATTERN_END in FILE:
            print FILE

0

Que tal str.split ()? Nada para importar.

import os

image_names = [f for f in os.listdir(path) if len(f.split('.jpg')) == 2]

2
Isso é muito semelhante à resposta dada por @gypsy
Sushanth 14/06

Isso parece ser semelhante à resposta de @ ramsey0 usando f.endswith('.jpg')(mas também selecionará filename.jpg.ext)
anjsimmo

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.