Python glob wiele typów plików

142

Czy istnieje lepszy sposób na użycie glob.glob w Pythonie, aby uzyskać listę wielu typów plików, takich jak .txt, .mdown i .markdown? W tej chwili mam coś takiego:

projectFiles1 = glob.glob( os.path.join(projectDir, '*.txt') )
projectFiles2 = glob.glob( os.path.join(projectDir, '*.mdown') )
projectFiles3 = glob.glob( os.path.join(projectDir, '*.markdown') )
Raptrex
źródło
1
Bardzo powiązane: stackoverflow.com/q/48181073/880783
bers

Odpowiedzi:

156

Może jest lepszy sposób, ale co powiesz na:

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

Być może jest inny sposób, więc poczekaj, aż ktoś inny poda lepszą odpowiedź.

user225312
źródło
19
files_grabbed = [glob.glob(e) for e in ['*.pdf', '*.cpp']]
Novitoll
10
Rozwiązanie Novitoll jest krótkie, ale kończy się tworzeniem zagnieżdżonych list.
robroc
9
zawsze możesz to zrobić;)[f for f_ in [glob.glob(e) for e in ('*.jpg', '*.mp4')] for f in f_]
AlexG
1
files_grabbed = [ glob.glob (e) for e in [' .pdf', '* .cpp']]
florisla
3
Powoduje to dwukrotne przejrzenie listy plików. W pierwszej iteracji sprawdza, czy jest plik * .pdf, aw drugiej plik * .cpp. Czy jest sposób, aby to zrobić w jednej iteracji? Sprawdzać stan łączny za każdym razem?
Ridhuvarshan
47
from glob import glob

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

print(files)

Jeśli musisz określić ścieżkę, zapętlaj wzorce dopasowania i zachowaj połączenie wewnątrz pętli dla uproszczenia:

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)
user2363986
źródło
44

glob zwraca listę: dlaczego po prostu nie uruchomić jej wiele razy i nie połączyć wyników?

from glob import glob
ProjectFiles = glob('*.txt') + glob('*.mdown') + glob('*markdown')
patrick-mooney
źródło
2
Jest to prawdopodobnie najbardziej czytelne podane rozwiązanie. Zmieniłbym przypadek ProjectFilesna projectFiles, ale świetne rozwiązanie.
Hans Goldman
40

Połącz wyniki:

import itertools as it, glob

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

Następnie:

for filename in multiple_file_types("*.txt", "*.sql", "*.log"):
    # do stuff
tzot
źródło
13
glob.glob -> glob.iglob tak, że łańcuch jest całkowicie leniwy iteratory oceniane
rodrigob
1
Znalazłem to samo rozwiązanie, ale o nim nie wiedziałem chain.from_iterable. Więc to jest podobne, ale mniej czytelny: it.chain(*(glob.iglob(pattern) for pattern in patterns)).
florisla
17

Tak wiele odpowiedzi, które sugerują globowanie tyle razy, ile liczba rozszerzeń, wolałbym globbować tylko raz:

from pathlib import Path

files = {p.resolve() for p in Path(path).glob("**/*") if p.suffix in [".c", ".cc", ".cpp", ".hxx", ".h"]}
BPL
źródło
15

w przypadku globu nie jest to możliwe. możesz użyć tylko:
* pasuje do wszystkiego
? dopasowuje dowolny pojedynczy znak
[seq] odpowiada dowolnemu znakowi w seq
[! seq] dopasowuje dowolny znak spoza seq

użyj os.listdir i wyrażenia regularnego, aby sprawdzić wzorce:

for x in os.listdir('.'):
  if re.match('.*\.txt|.*\.sql', x):
    print x
chrześcijanin
źródło
10
zakończ swoje wyrażenie regularne
znakiem
1
Podoba mi się to podejście - jeśli ekspresyjność globu nie jest wystarczająco potężna, uaktualnij do mocniejszego systemu regex, nie hakuj go używając np. itertoolsPonieważ kolejne zmiany wzorców również muszą być hacky (powiedzmy, że chcesz zezwolić na duże i małe litery) . Aha, i pisanie mogłoby być czystsze'.*\.(txt|sql)'
metakermit
Czy jest jakiś powód, aby preferować os.listdir („.”) Zamiast glob.iglob („ . ”)?
Mr.WorshipMe
14

Na przykład dla *.mp3i *.flacna wielu folderach możesz wykonać:

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

Pomysł można rozszerzyć na więcej rozszerzeń plików, ale musisz sprawdzić, czy kombinacje nie będą pasować do żadnego innego niechcianego rozszerzenia plików, które możesz mieć w tych folderach. Więc uważaj z tym.

Aby automatycznie połączyć dowolną listę rozszerzeń w jeden wzorzec glob, możesz wykonać następujące czynności:

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]*
feqwix
źródło
6

Jedna linijka, po prostu dla diabła z tym ..

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]

wynik:

['C:\\multi_pattern_glob_one_liner\\dummy_txt.txt', 'C:\\multi_pattern_glob_one_liner\\dummy_bat.bat']
Gil-Mor
źródło
4

Po przyjściu tutaj po pomoc stworzyłem własne rozwiązanie i chciałem się nim podzielić. Opiera się na odpowiedzi użytkownika2363986, ale myślę, że jest to bardziej skalowalne. Oznacza to, że jeśli masz 1000 rozszerzeń, kod nadal będzie wyglądał dość elegancko.

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
Hans Goldman
źródło
Nie działa na mnie. UżywamdirectoryPath = "/Users/bla/bla/images_dir*."
NeStack
Potrzebuję więcej informacji, aby to za Ciebie debugować ... Czy otrzymujesz wyjątek? Ponadto, jeśli korzystasz z systemu Windows, ta ścieżka nie wygląda tak, jakby działała (brak litery dysku).
Hans Goldman,
4
files = glob.glob('*.txt')
files.extend(glob.glob('*.dat'))
Derek White
źródło
4
Dobre odpowiedzi zawierają również wyjaśnienie kodu, a być może nawet pewne uzasadnienie kodu.
SunSparc
4

Chociaż domyślna glob w Pythonie nie podąża za globem Basha, możesz to zrobić z innymi bibliotekami. Możemy włączyć szelki w pliku glob wcmatch .

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

Możesz nawet użyć rozszerzonych wzorców glob, jeśli wolisz:

from wcmatch import glob
>>> glob.glob('*.@(md|ini)', flags=glob.EXTGLOB)
['LICENSE.md', 'README.md', 'tox.ini']
anonimowy użytkownik
źródło
To nie bierze recursiveflagi
Shamoon
@Shamoon Nie, to bierze glob.GLOBSTARflagę
faceelessuser
3

Wydałem Formic, który implementuje wiele dołączeń w podobny sposób jak FileSet i Globs Apache Ant .

Wyszukiwanie można wdrożyć:

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

Ponieważ zaimplementowano pełną wersję Ant glob, możesz dołączyć różne katalogi do każdego wzorca, więc możesz wybrać tylko te pliki .txt w jednym podkatalogu, a .markdown w innym, na przykład:

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

Mam nadzieję, że to pomoże.

Andrew Alcock
źródło
3

Poniższe funkcje _globglobalne dla wielu rozszerzeń plików.

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")
Tim Fuller
źródło
3

To jest pathlibrozwiązanie 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("~"))

Ignoruje również wszystkie nazwy plików zaczynające się od ~.

Winand
źródło
3

Oto jednowierszowy wariant odpowiedzi Pata ze zrozumieniem listy (który obejmuje również to, że chciałeś globować w określonym katalogu projektu):

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

Zapętlasz rozszerzenia ( for ext in exts), a następnie dla każdego rozszerzenia pobierasz każdy plik pasujący do wzorca glob ( for f in glob.glob(os.path.join(project_dir, ext)).

To rozwiązanie jest krótkie i nie zawiera żadnych niepotrzebnych pętli for, zagnieżdżonych list składanych ani funkcji zaśmiecających kod. Po prostu czysty, wyrazisty, pytoniczny Zen .

To rozwiązanie pozwala mieć niestandardową listę, extsktóre można zmienić bez konieczności aktualizowania kodu. (To zawsze dobra praktyka!)

Zrozumienie listy jest takie samo, jak w rozwiązaniu Laurenta (na które głosowałem). Ale argumentowałbym, że zwykle nie jest konieczne wyodrębnianie pojedynczego wiersza do oddzielnej funkcji, dlatego zapewniam to jako alternatywne rozwiązanie.

Premia:

Jeśli chcesz przeszukać nie tylko pojedynczy katalog, ale także wszystkie podkatalogi, możesz przekazać recursive=Truei użyć symbolu globu wielu katalogów ** 1 :

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

Spowoduje to wywołanie glob.glob('<project_dir>/**/*.txt', recursive=True)i tak dalej dla każdego rozszerzenia.

1 Technicznie rzecz biorąc, **symbol globu po prostu dopasowuje jeden lub więcej znaków, w tym ukośnik / (w przeciwieństwie do pojedynczego *symbolu globu). W praktyce musisz tylko pamiętać, że tak długo, jak **otaczasz ukośnikami (separatorami ścieżek), dopasowuje zero lub więcej katalogów.

scholer
źródło
2

Nie glob, ale oto inny sposób używania rozumienia listy:

extensions = 'txt mdown markdown'.split()
projectFiles = [f for f in os.listdir(projectDir) 
                  if os.path.splitext(f)[1][1:] in extensions]
joemaller
źródło
1

Możesz spróbować stworzyć ręczną listę porównującą rozszerzenia istniejących z tymi, których potrzebujesz.

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)
thegauraw
źródło
1

Aby globmieć wiele typów plików, musisz glob()kilkakrotnie wywołać funkcję w pętli. Ponieważ ta funkcja zwraca listę, musisz połączyć listy.

Na przykład ta funkcja wykonuje zadanie:

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

Proste użycie:

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

Możesz także użyć glob.iglob()iteratora:

Zwraca iterator, który zwraca te same wartości co glob () bez faktycznego przechowywania ich wszystkich jednocześnie.

def iglob_filetypes(root_dir, *patterns):
    return (path
            for pattern in patterns
            for path in glob.iglob(os.path.join(root_dir, pattern)))
Laurent LAPORTE
źródło
1

Użyj listy rozszerzeń i powtórz

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)
Projesh Bhoumik
źródło
0

Możesz użyć filtra:

import os
import glob

projectFiles = filter(
    lambda x: os.path.splitext(x)[1] in [".txt", ".mdown", ".markdown"]
    glob.glob(os.path.join(projectDir, "*"))
)
LK__
źródło
0

Możesz również użyć w ten reduce()sposób:

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

tworzy to listę glob.glob()dla każdego wzorca i redukuje je do jednej listy.

cyht
źródło
0

Jedna glob, wiele rozszerzeń ... ale rozwiązanie niedoskonałe (może pasować do innych plików).

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)
colllin
źródło
0

Miałem ten sam problem i to właśnie wymyśliłem

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))
Justin
źródło
0

Jeszcze inne rozwiązanie (użyj, globaby uzyskać ścieżki przy użyciu wielu dopasowań patternsi połączyć wszystkie ścieżki w jedną listę za pomocą reducei add):

import functools, glob, operator
paths = functools.reduce(operator.add, [glob.glob(pattern) for pattern in [
    "path1/*.ext1",
    "path2/*.ext2"]])
Petr Vepřek
źródło
0

Jeśli używasz, pathlibspróbuj tego:

import pathlib

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

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

print(list(files))
qik
źródło
0

Po wynikach testów empirycznych okazało się, że glob.globnie jest to lepszy sposób na odfiltrowanie plików według ich rozszerzeń. Oto niektóre z powodów:

  • Globalny „ język ” nie pozwala na idealne określenie wielokrotnego rozszerzenia.
  • Poprzedni punkt powoduje uzyskiwanie nieprawidłowych wyników w zależności od rozszerzeń plików.
  • Udowodniono empirycznie, że metoda globbingu jest wolniejsza niż większość innych metod.
  • Nawet jeśli jest to dziwne, nawet inne obiekty systemów plików mogą mieć „ rozszerzenia ”, a także foldery.

Przetestowałem (pod kątem poprawności i wydajności w czasie) następujące 4różne metody filtrowania plików według rozszerzeń i umieszczam je w 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

Uruchamiając powyższy kod na moim laptopie uzyskałem następujące auto-wyjaśnienia.

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.

Najszybszy sposób na odfiltrowanie plików według rozszerzeń jest nawet najbrzydszy. Czyli zagnieżdżone forpętle i stringporównanie przy użyciu endswith()metody.

Ponadto, jak widać, algorytmy globbingu (ze wzorcem E:\x\y\z\**/*[py][pyc]) nawet z 2podanym tylko rozszerzeniem ( pyi pyc) zwracają również niepoprawne wyniki.

Giova
źródło
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)
Sway Wu
źródło
Cześć Sway Wu, witaj. Proszę rozważyć dodanie wyjaśnienia.
Tiago Martins Peres 李大仁
-1

To powinno działać:

import glob
extensions = ('*.txt', '*.mdown', '*.markdown')
for i in extensions:
    for files in glob.glob(i):
        print (files)
jdnoon
źródło
-1

Na przykład:

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

Funkcja:

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
Jayhello
źródło