2010-04-16 14 views
7

Gli utenti della mia app possono configurare il layout di determinati file tramite una stringa di formato.Python: Converti stringa di formato in espressione regolare

Ad esempio, il valore di configurazione i specifica utente potrebbe essere:

layout = '%(group)s/foo-%(locale)s/file.txt' 

io ora bisogno di trovare tutti questi file già esistenti. Questo sembra abbastanza facile utilizzando il modulo glob:

glob_pattern = layout % {'group': '*', 'locale': '*'} 
glob.glob(glob_pattern) 

Tuttavia, ora arriva la parte più difficile: Data la lista dei risultati glob, ho bisogno di ottenere tutti quei nomi di file-parti che hanno abbinato un determinato segnaposto, per esempio tutti i diversi valori "locali".

Ho pensato di generare un'espressione regolare per la stringa di formato che potrei quindi confrontare con l'elenco dei risultati glob (o quindi eventualmente saltare il glob e fare tutto il matching da solo).

Ma non riesco a trovare un buon modo per creare la regex con le acquisizioni di gruppo appropriate e l'escape del resto dell'input.

Per esempio, questo potrebbe darmi una regex che corrisponde alle impostazioni internazionali:

regex = layout % {'group': '.*', 'locale': (.*)} 

Ma per essere sicuri la regex è valido, ho bisogno di passare attraverso re.escape(), che poi sfugge anche la sintassi regex che ho appena inserito. Chiamare re.escape() rovina prima la stringa di formato.

So che c'è fnmatch.translate(), che mi dà anche una regex, ma non quella che restituisce i gruppi corretti.

C'è un buon modo per fare questo, senza un hack come sostituire i segnaposti con un valore univoco sicuro per la regex ecc.?

C'è forse un modo (forse una libreria di terze parti?) Che consente di sezionare una stringa di formato in un modo più flessibile, ad esempio suddividendo la stringa nelle posizioni dei segnaposto?

risposta

2

Poiché si utilizzano segnaposto con nome, utilizzerei gruppi denominati. Questo sembra funzionare:

import re 
UNIQ='_UNIQUE_STRING_' 
class MarkPlaceholders(dict): 
    def __getitem__(self, key): 
     return UNIQ+('(?P<%s>.*?)'%key)+UNIQ 

def format_to_re(format): 
    parts = (format % MarkPlaceholders()).split(UNIQ) 
    for i in range(0, len(parts), 2): 
     parts[i] = re.escape(parts[i]) 
    return ''.join(parts) 

e poi alla prova:

>>> layout = '%(group)s/foo-%(locale)s/file.txt' 
>>> print format_to_re(layout) 
(?P<group>.*?)\/foo\-(?P<locale>.*?)\/file\.txt 
>>> pattern = re.compile(format_to_re(layout)) 
>>> print pattern.match('something/foo-en-gb/file.txt').groupdict() 
{'locale': 'en-gb', 'group': 'something'} 
+0

avevo sperato di trovare un modo diverso rispetto all'utilizzo di un identificativo univoco, ma questo è uno spin interessante su questo approccio. In particolare, mi piace il fatto che mi occorrerà un solo separatore univoco, anziché uno per ogni campo che deve corrispondere a un'espressione regolare diversa. – miracle2k

+0

Se il separatore univoco ti preoccupa troppo puoi sempre includere un numero e incrementare il numero fino a ottenere qualcosa che non è nella stringa. – Duncan

+0

ok, questo funziona per le stringhe, sarebbe possibile farlo funzionare per più costrutti? come analizzare "nodo% (id) 03d" su "nodo (? P \ d \ d \ d)" –

1

Puoi provare questo; funziona attorno ai tuoi problemi di fuga.

unique = '_UNIQUE_STRING_' 
assert unique not in layout 
regexp = re.escape(layout % {'group': unique, 'locale': unique}).replace(unique, '(.*)') 
Problemi correlati