fnmatch и рекурсивное совпадение пути с `**`
Есть ли какой-либо встроенный или простой способ рекурсивного сопоставления путей с двойной звездочкой, например, как это делает zsh?
Например, с
path = 'foo/bar/ham/spam/eggs.py'
Я могу использовать fnmatch, чтобы проверить это с
fnmatch(path, 'foo/bar/ham/*/*.py'
Хотя я хотел бы иметь возможность сделать:
fnmatch(path, 'foo/**/*.py')
Я знаю, что fnmatch отображает свой шаблон на регулярное выражение, поэтому в случае слов я могу бросить свой собственный fnmatch с дополнительными **
шаблон, но, возможно, есть более простой способ
2 ответа
Если вы внимательно посмотрите на исходный код fnmatch, он отобразит *
в .*
и, следовательно, не заботится о разделителях каталогов /
- в отличие от оболочек UNIX:
while i < n:
c = pat[i]
i = i+1
if c == '*':
res = res + '.*'
elif c == '?':
res = res + '.'
elif c == '[':
...
таким образом
>>> fnmatch.fnmatch('a/b/d/c', 'a/*/c')
True
>>> fnmatch.fnmatch('a/b/d/c', 'a/*************c')
True
Для варианта fnmatch, который работает с путями, вы можете использовать библиотеку под названием wcmatch, которая реализуетglobmatch
функция, которая соответствует пути с той же логикой, что и glob
сканирует файловую систему с помощью. Вы можете управлять включенными функциями с помощью флагов, в этом случае мы включаемGLOBSTAR
(с помощью **
для рекурсивного поиска в каталоге).
>>> from wcmatch import glob
>>> glob.globmatch('some/file/path/filename.txt', 'some/**/*.txt', flags=glob.GLOBSTAR)
True
Если вы можете жить без использования цикла os.walk, попробуйте:
Я лично использую glob2:
import glob2
files = glob2.glob(r'C:\Users\**\iTunes\**\*.mp4')
Приложение:
Начиная с Python 3.5, собственный модуль glob поддерживает рекурсивное сопоставление с образцом:
import glob
files = glob.iglob(r'C:\Users\**\iTunes\**\*.mp4', recursive=True)
Этот фрагмент добавляет совместимость для **
import re
from functools import lru_cache
from fnmatch import translate as fnmatch_translate
@lru_cache(maxsize=256, typed=True)
def _compile_fnmatch(pat):
# fixes fnmatch for recursive ** (for compatibilty with Path.glob)
pat = fnmatch_translate(pat)
pat = pat.replace('(?s:.*.*/', '(?s:(^|.*/)')
pat = pat.replace('/.*.*/', '.*/')
return re.compile(pat).match
def fnmatch(name, pat):
return _compile_fnmatch(str(pat))(str(name)) is not None