Desidero poter abbinare un modello nel formato glob
a un elenco di stringhe piuttosto che a file effettivi nel filesystem. C'è un modo per farlo o convertire facilmente un modello glob
in un'espressione regolare?Glob python ma rispetto a un elenco di stringhe anziché al filesystem
risposta
I buoni artisti copiano; grandi artisti steal.
Ho vinto;)
fnmatch.translate
traduce gocce ?
e *
per RegEx .
e .*
rispettivamente. L'ho ottimizzato per non farlo.
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)'
Questo à la fnmatch.filter
, sia re.match
e re.search
lavoro.
def glob_filter(names,pat):
return (name for name in names if re.match(glob2re(pat),name))
modelli Glob e le stringhe trovate su questa pagina di prova Pass.
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))))
molto perspicace - grazie! –
Grande scoop! Sì, tradurre il modello in uno che ignora i separatori di percorsi è una grande idea. Nota che non gestisce 'os.sep' o' os.altsep', ma dovrebbe essere abbastanza facile da regolare per quello. –
Grazie a @ martijn-pieters –
non importa, l'ho trovato. Voglio il modulo fnmatch.
Oh wait - fnmatch non gestisce la segmentazione del percorso ... sigh –
Puoi fornire esempi, dove 'fnmatch' non gestisce il tuo caso? –
@BhargavRao:' glob.glob() 'applica i tracciati agli elementi del percorso separatamente –
Mentre fnmatch.fnmatch
può essere utilizzato direttamente per verificare se un modello corrisponde a un nome di file o no, è anche possibile utilizzare il metodo fnmatch.translate
per generare la regex fuori del data fnmatch
modello:
>>> import fnmatch
>>> fnmatch.translate('*.txt')
'.*\\.txt\\Z(?ms)'
Dal documenation:
fnmatch.translate(pattern)
Ritorna il modello della shell in stile convertito in un'espres regolare fissione.
Il modulo glob
utilizza le fnmatch
module per singoli elementi di percorso.
Ciò significa che il percorso è suddiviso nel nome della directory e il nome del file, e se il nome della directory contiene caratteri meta (contiene uno dei caratteri [
, *
o ?
) allora questi vengono espanse ricorsivamente.
Se si dispone di una lista di stringhe che sono semplici nomi di file, quindi solo utilizzando il fnmatch.filter()
function è sufficiente:
import fnmatch
matching = fnmatch.filter(filenames, pattern)
ma se contengono percorsi completi, è necessario lavorare di più come l'espressione regolare generato doesn t prendere in considerazione i segmenti di percorso (i caratteri jolly non escludono i separatori e non vengono modificati per la corrispondenza del percorso multipiattaforma).
È possibile costruire un semplice trie dai sentieri, quindi abbinare il vostro modello contro che:
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)]
Questo boccone può rapidamente trovare corrispondenze con gocce ovunque lungo il percorso:
>>> 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']
su Python 3.4+ puoi semplicemente usare PurePath.match
.
pathlib.PurePath(path_string).match(pattern)
su Python 3.3 o precedenti (compresa la 2.x), ottenere pathlib
from PyPI.
Si noti che per ottenere risultati indipendenti dalla piattaforma (che dipenderanno perché si sta eseguendo questo) che ci si vuole dichiarare esplicitamente PurePosixPath
o PureWindowsPath
.
Perché diavolo è davvero downvoted? – Stilgar
Puoi aggiungere più contenuto e contesto per favore? Come nell'output atteso per un file system fittizio –
Quindi vuoi dare un elenco di file come python 'list' a glob, invece di prelevarlo dal filesystem? –