Python glob декілька типів файлів


142

Чи є кращий спосіб використовувати glob.glob у python, щоб отримати список декількох типів файлів, таких як .txt, .mdown та .markdown? Зараз у мене є щось подібне:

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

1
Дуже пов’язані: stackoverflow.com/q/48181073/880783
bers

Відповіді:


156

Можливо, є кращий спосіб, але як щодо:

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

Можливо, є інший спосіб, тому зачекайте, якщо хтось інший придумає кращу відповідь.


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

10
Рішення Novitoll коротке, але воно закінчується створенням вкладених списків.
robroc

9
ви завжди могли це зробити;)[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
Це прокручується двічі через список файлів. У першій ітерації він перевіряє * .pdf, а в другій перевіряє * .cpp. Чи є спосіб це зробити за одну ітерацію? Кожен раз перевіряйте комбінований стан?
Ridhuvarshan

47
from glob import glob

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

print(files)

Якщо вам потрібно вказати шлях, переведіть схему відповідності та збережіть з'єднання всередині циклу для простоти:

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)

44

glob повертає список: чому б просто не запустити його кілька разів і об'єднати результати?

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

2
Це, можливо, найбільш читабельне рішення. Я хотів би змінити справу ProjectFilesдо projectFiles, але відмінне рішення.
Ганс Голдман

40

Закріпіть результати:

import itertools as it, glob

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

Тоді:

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

13
glob.glob -> glob.iglob, так що ланцюжок ітераторів повністю ліниво оцінюється
rodrigob

1
Я знайшов те саме рішення, але не знав про це chain.from_iterable. Так що це схоже, але менш читається: it.chain(*(glob.iglob(pattern) for pattern in patterns)).
florisla

17

Стільки відповідей, що пропонують глобалізацію стільки разів, скільки кількість розширень, замість цього я віддаю перевагу глобалізації лише один раз:

from pathlib import Path

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

15

з глобусом це неможливо. ви можете використовувати лише:
* відповідає всім
? відповідає будь-якому одному символу
[seq], відповідає будь-якому символу в seq
[! seq], відповідає будь-якому символу, не в послідовності

використовувати os.listdir та regexp для перевірки шаблонів:

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


1
Мені подобається такий підхід - якщо виразність глобуса недостатньо потужна, оновіть до більш потужної системи регулярних виразів, не рушіть на ній, наприклад, itertoolsтому що наступні зміни шаблону також повинні бути хакітними (скажімо, ви хочете дозволити верхній і нижній регістри) . О, і писати може бути чистіше'.*\.(txt|sql)'
metakermit

Чи є якась причина віддавати перевагу os.listdir ('.') Над glob.iglob (' . ')?
Mr.WorshipMe

14

Наприклад, для *.mp3та *.flacв кількох папках ви можете:

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

Ідею можна поширити на додаткові розширення файлів, але ви повинні перевірити, що комбінації не збігаються з будь-яким іншим небажаним розширенням файлу, яке може бути у цих папках. Отже, будьте обережні з цим.

Щоб автоматично поєднати довільний список розширень в єдиний шаблон, ви можете зробити наступне:

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

Однолінійний, просто для біса ..

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]

вихід:

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

4

Прийшовши сюди за допомогою, я прийняв власне рішення і хотів поділитися ним. Він заснований на відповіді user2363986, але я думаю, що це більш масштабовано. Це означає, що якщо у вас 1000 розширень, код все одно буде виглядати дещо елегантно.

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

Не працює для мене. Я використовуюdirectoryPath = "/Users/bla/bla/images_dir*."
NeStack

Мені знадобиться більше інформації, щоб налагодити це для вас ... Ви отримуєте виняток? Крім того, якщо ви перебуваєте в Windows, цей шлях не виглядає так, як він би працював (відсутня літера диска).
Ганс Голдман

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

4
Хороші відповіді також дають деяке пояснення коду і, можливо, навіть деякі ваші міркування за кодом.
SunSparc

4

Хоча глоб за замовчуванням Python насправді не слідує за глобусом Баша, ви можете це зробити з іншими бібліотеками. Ми можемо ввімкнути брекети в глобальній програмі wcmatch .

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

Ви навіть можете використовувати розширені шаблони глобусів, якщо це ваша перевага:

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

Це не приймає recursiveпрапор
Shamoon

@Shamoon Ні, він приймає glob.GLOBSTARпрапор
facelessuser

3

Я випустив Formic, який реалізує кілька включень , аналогічно FileSet і Globs Apache Ant .

Пошук можна здійснити:

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

Оскільки реалізований повний глобуль Ant, ви можете включати різні каталоги з кожним шаблоном, тож ви можете вибирати лише ті .txt файли в одному підкаталозі та .markdown в іншому, наприклад:

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

Я сподіваюся, що це допомагає.


3

Наступні функції _globглобусів для декількох розширень файлів.

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

Це рішення Python 3.4+ pathlib:

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("~"))

Також він ігнорує всі назви файлів, починаючи з ~.


3

Ось однолінійний варіант розуміння списку відповіді Пата (який також включає те, що ви хотіли взяти участь у конкретному каталозі проектів):

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

Ви переходите до розширень ( for ext in exts), а потім за кожне розширення ви берете кожен файл, що відповідає глобальному шаблону ( for f in glob.glob(os.path.join(project_dir, ext)).

Це рішення є коротким і без зайвих циклів, вкладених списків або функцій, що захаращують код. Просто чистий, виразний, пітонічний дзен .

Це рішення дозволяє мати спеціальний список, extsякий можна змінити, не потребуючи оновлення коду. (Це завжди хороша практика!)

Зрозуміння списку - це те саме, що використовується у рішенні Лорана (за який я проголосував). Але я заперечую, що зазвичай не потрібно виділяти окремий рядок на окрему функцію, тому я пропоную це як альтернативне рішення.

Бонус:

Якщо вам потрібно здійснити пошук не лише одного каталогу, а й усіх підкаталогів, ви можете передати recursive=Trueта використовувати глобальний символ глобальної версії ** 1 :

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

Це буде викликати glob.glob('<project_dir>/**/*.txt', recursive=True)і так далі для кожного розширення.

1 Технічно **символ глобуса просто відповідає одному або більше символів, включаючи нахил вперед / (на відміну від сингулярного *символу глобуса). На практиці вам просто потрібно пам’ятати, що доки ви оточуєте **косою косою рискою (роздільники контурів), вона відповідає нулю або більше каталогів.


2

Ні glob, але ось інший спосіб використання списку розуміння:

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

1

Ви можете спробувати скласти список вручну, порівнюючи розширення існуючого з необхідним.

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

Для globдекількох типів файлів потрібно glob()кілька разів викликати функцію в циклі. Оскільки ця функція повертає список, вам потрібно об'єднати списки.

Наприклад, ця функція виконує роботу:

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

Просте використання:

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

Ви також можете використовувати glob.iglob()ітератор:

Поверніть ітератор, який дає ті самі значення, що і glob (), фактично не зберігаючи їх одночасно.

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

1

Скористайтеся списком розширення та повторіть його

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

Ви можете використовувати фільтр:

import os
import glob

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

0

Ви також можете використовувати reduce()так:

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

це створює список glob.glob()для кожного шаблону і зводить їх до одного списку.


0

Один глобус, багато розширень ..., але недосконале рішення (може відповідати іншим файлам).

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

У мене було те саме питання, і ось що я придумав

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

Ще одне рішення (використовуйте globдля отримання шляхів за допомогою декількох збігів patternsта об'єднайте всі шляхи в єдиний список за допомогою reduceта add):

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

0

Якщо ви використовуєте, pathlibспробуйте це:

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

За результатами, отриманими на основі емпіричних тестів, виявилося, що glob.globце не кращий спосіб відфільтрувати файли за їх розширеннями. Деякі причини:

  • Глобальна " мова " не дозволяє ідеально вказати кратне розширення.
  • Колишній пункт призводить до отримання невірних результатів залежно від розширень файлів.
  • Емпірично доведено, що метод глобусу є повільнішим, ніж більшість інших методів.
  • Навіть якщо це дивно, навіть інші об'єкти файлових систем можуть мати " розширення ", папки теж.

Я перевірив (на предмет коректності та ефективності в часі) наступні 4різні методи фільтрації файлів за розширеннями та розміщення їх у 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

Запустивши код вище на своєму ноутбуці, я отримав такі результати автоексплікації.

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.

Найшвидший спосіб фільтрувати файли за допомогою розширень, буває навіть самим некрасивим. Що таке вкладені forпетлі та stringпорівняння за допомогою endswith()методу.

Більше того, як ви бачите, алгоритми глобалізації (із зразком E:\x\y\z\**/*[py][pyc]) навіть із лише 2розширенням, що задано ( pyі pyc), повертають також неправильні результати.


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 Ву, ласкаво просимо. Будь ласка, розгляньте, як додати пояснення.
Tiago Martins Peres 李大仁

-1

Це повинно працювати:

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

-1

Наприклад:

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

Функція:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.