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
Берс

Ответы:


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 короткое, но в итоге оно создает вложенные списки.
Роброк

9
Вы всегда можете сделать это;)[f for f_ in [glob.glob(e) for e in ('*.jpg', '*.mp4')] for f in f_]
AlexG

1
files_grabbed = [ glob.glob (e) для e в [' .pdf', '* .cpp']]
флорисла,

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)

45

glob возвращает список: почему бы просто не запустить его несколько раз и объединить результаты?

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

3
Это, пожалуй, самое читаемое решение. Я бы поменял регистр 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)).
Флорисла,

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] соответствует любому символу не в seq

используйте os.listdir и регулярное выражение для проверки шаблонов:

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

10
заканчивайте свое регулярное выражение $, чтобы соответствовать только концу имени файла
ThiefMaster

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

Есть ли причина предпочитать os.listdir ('.'), А не glob.iglob (' . ')?
Mr.WorshipMe

14

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

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

Идея может быть расширена на большее количество расширений файлов, но вы должны убедиться, что комбинации не будут совпадать с любым другим нежелательным расширением файла, которое у вас может быть в этих папках. Так что будьте осторожны с этим.

Чтобы автоматически объединить произвольный список расширений в один шаблон glob, вы можете сделать следующее:

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 по умолчанию не следует за глобусом Bash, вы можете сделать это с другими библиотеками. Мы можем включить скобки в глобусе wcmatch .

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

Вы даже можете использовать расширенные шаблоны glob, если это ваше предпочтение:

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

Это не берет recursiveфлаг
Shamoon

@ Shamoon Нет, это берет glob.GLOBSTARфлаг
безликий пользователь

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

Следующие функции _globglobs для нескольких расширений файлов.

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

Это pathlibрешение 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("~"))

Также игнорируются все имена файлов, начинающиеся с ~.


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

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

Это короткое решение , без лишних циклов for, вложенных списочных пониманий или функций, которые загромождают код. Просто чистый, выразительный, питонический дзен .

Это решение позволяет вам иметь собственный список, 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 Ву, добро пожаловать. Пожалуйста, рассмотрите возможность добавления объяснения.
Тьяго Мартинс Перес 李大仁

-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.