recursively from files filename all python regex python-2.7 glob

from - Python glob pero contra una lista de cadenas en lugar del sistema de archivos



glob stock (5)

Quiero poder hacer coincidir un patrón en formato glob con una lista de cadenas, en lugar de con archivos reales en el sistema de archivos. ¿Hay alguna manera de hacer esto, o convertir un patrón glob fácilmente a una expresión regular?


El módulo glob usa el módulo fnmatch para elementos de ruta individuales .

Eso significa que la ruta se divide en el nombre del directorio y el nombre del archivo, y si el nombre del directorio contiene metacaracteres (contiene alguno de los caracteres [ , * o ? ), Estos se expanden recursivamente .

Si tiene una lista de cadenas que son nombres de archivos simples, entonces basta con usar la función fnmatch.filter() :

import fnmatch matching = fnmatch.filter(filenames, pattern)

pero si contienen rutas completas, debe trabajar más, ya que la expresión regular generada no tiene en cuenta los segmentos de ruta (los comodines no excluyen los separadores ni se ajustan para la coincidencia de rutas entre plataformas).

Puedes construir un trie simple a partir de las rutas, luego hacer coincidir tu patrón con eso:

import fnmatch import glob import os.path from itertools import product # Cross-Python dictionary views on the keys if hasattr(dict, ''viewkeys''): # Python 2 def _viewkeys(d): return d.viewkeys() else: # Python 3 def _viewkeys(d): return d.keys() def _in_trie(trie, path): """Determine if path is completely in trie""" current = trie for elem in path: try: current = current[elem] except KeyError: return False return None in current def find_matching_paths(paths, pattern): """Produce a list of paths that match the pattern. * paths is a list of strings representing filesystem paths * pattern is a glob pattern as supported by the fnmatch module """ if os.altsep: # normalise pattern = pattern.replace(os.altsep, os.sep) pattern = pattern.split(os.sep) # build a trie out of path elements; efficiently search on prefixes path_trie = {} for path in paths: if os.altsep: # normalise path = path.replace(os.altsep, os.sep) _, path = os.path.splitdrive(path) elems = path.split(os.sep) current = path_trie for elem in elems: current = current.setdefault(elem, {}) current.setdefault(None, None) # sentinel matching = [] current_level = [path_trie] for subpattern in pattern: if not glob.has_magic(subpattern): # plain element, element must be in the trie or there are # 0 matches if not any(subpattern in d for d in current_level): return [] matching.append([subpattern]) current_level = [d[subpattern] for d in current_level if subpattern in d] else: # match all next levels in the trie that match the pattern matched_names = fnmatch.filter({k for d in current_level for k in d}, subpattern) if not matched_names: # nothing found return [] matching.append(matched_names) current_level = [d[n] for d in current_level for n in _viewkeys(d) & set(matched_names)] return [os.sep.join(p) for p in product(*matching) if _in_trie(path_trie, p)]

Este bocado puede encontrar coincidencias rápidamente usando globos en cualquier lugar a lo largo del camino:

>>> paths = [''/foo/bar/baz'', ''/spam/eggs/baz'', ''/foo/bar/bar''] >>> find_matching_paths(paths, ''/foo/bar/*'') [''/foo/bar/baz'', ''/foo/bar/bar''] >>> find_matching_paths(paths, ''/*/bar/b*'') [''/foo/bar/baz'', ''/foo/bar/bar''] >>> find_matching_paths(paths, ''/*/[be]*/b*'') [''/foo/bar/baz'', ''/foo/bar/bar'', ''/spam/eggs/baz'']


En Python 3.4+ puedes usar PurePath.match .

pathlib.PurePath(path_string).match(pattern)

En Python 3.3 o anterior (incluido 2.x), obtenga pathlib de PyPI .

Tenga en cuenta que para obtener resultados independientes de la plataforma (que dependerán de la razón por la que esté ejecutando esto), querría indicar explícitamente PurePosixPath o PureWindowsPath .


Si bien fnmatch.fnmatch se puede usar directamente para verificar si un patrón coincide con un nombre de archivo o no, también puede usar el método fnmatch.translate para generar la expresión regular del patrón de fnmatch dado:

>>> import fnmatch >>> fnmatch.translate(''*.txt'') ''.*//.txt//Z(?ms)''

De la documenation :

fnmatch.translate(pattern)

Devuelve el patrón de estilo de shell convertido en una expresión regular.


no importa lo encontré. Quiero el módulo fnmatch .


Copia de buenos artistas; Los grandes artistas steal .

Robé ;)

fnmatch.translate traduce globs ? y * a expresiones regulares . y .* respectivamente. Lo pellizqué para que no lo hiciera.

import re def glob2re(pat): """Translate a shell PATTERN to a regular expression. There is no way to quote meta-characters. """ i, n = 0, len(pat) res = '''' while i < n: c = pat[i] i = i+1 if c == ''*'': #res = res + ''.*'' res = res + ''[^/]*'' elif c == ''?'': #res = res + ''.'' res = res + ''[^/]'' elif c == ''['': j = i if j < n and pat[j] == ''!'': j = j+1 if j < n and pat[j] == '']'': j = j+1 while j < n and pat[j] != '']'': j = j+1 if j >= n: res = res + ''//['' else: stuff = pat[i:j].replace(''//',''////') i = j+1 if stuff[0] == ''!'': stuff = ''^'' + stuff[1:] elif stuff[0] == ''^'': stuff = ''//' + stuff res = ''%s[%s]'' % (res, stuff) else: res = res + re.escape(c) return res + ''/Z(?ms)''

Este uno a la fnmatch.filter , tanto re.match como re.search trabajo.

def glob_filter(names,pat): return (name for name in names if re.match(glob2re(pat),name))

Patrones y cadenas globales que se encuentran en esta página pasan la prueba.

pat_dict = { ''a/b/*/f.txt'': [''a/b/c/f.txt'', ''a/b/q/f.txt'', ''a/b/c/d/f.txt'',''a/b/c/d/e/f.txt''], ''/foo/bar/*'': [''/foo/bar/baz'', ''/spam/eggs/baz'', ''/foo/bar/bar''], ''/*/bar/b*'': [''/foo/bar/baz'', ''/foo/bar/bar''], ''/*/[be]*/b*'': [''/foo/bar/baz'', ''/foo/bar/bar''], ''/foo*/bar'': [''/foolicious/spamfantastic/bar'', ''/foolicious/bar''] } for pat in pat_dict: print(''pattern :/t{}/nstrings :/t{}''.format(pat,pat_dict[pat])) print(''matched :/t{}/n''.format(list(glob_filter(pat_dict[pat],pat))))