2010-04-16 18 views
7

Die Benutzer meiner App können das Layout bestimmter Dateien über eine Formatzeichenfolge konfigurieren.Python: Format-String in regulären Ausdruck konvertieren

Zum Beispiel kann der Konfigurationswert der Benutzer legt fest, könnte sein:

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

Ich brauche jetzt alle diese Dateien zu finden, die bereits vorhanden sind. Das scheint einfach genug, die glob Modul:

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

jedoch nun der schwierige Teil kommt: Die Liste der glob Ergebnisse gegeben, ich brauche alle diese Dateinamen-Teile zu erhalten, die einen bestimmten Platzhalter abgestimmt, zum Beispiel all die verschiedenen "locale" -Werte.

Ich dachte, ich würde einen regulären Ausdruck für die Formatzeichenfolge generieren, die ich dann mit der Liste der Glob-Ergebnisse vergleichen könnte (oder dann möglicherweise überspringen Glob und alle passenden selbst tun).

Aber ich finde keine nette Möglichkeit, die Regex mit den richtigen Gruppenaufnahmen zu erstellen und den Rest der Eingabe zu entkommen.

Zum Beispiel könnte dies geben Sie mir einen regulären Ausdruck, der die Schauplätze übereinstimmt:

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

Aber sicher sein die Regex gültig ist, muss ich es durch re.escape() zu übergeben, die dann entweicht auch die Regex-Syntax, die ich gerade eingefügt habe. Durch den Aufruf von re.escape() wird zuerst die Formatzeichenfolge gelöscht.

Ich weiß, es gibt fnmatch.translate(), die mir sogar eine Regex geben würde - aber nicht eine, die die richtigen Gruppen zurückgibt.

Gibt es einen guten Weg, dies zu tun, ohne einen Hack wie Ersetzen der Platzhalter mit einem Regex-sicheren einzigartigen Wert etc.?

Gibt es möglicherweise einen Weg (eine Third-Party-Bibliothek vielleicht?), Die es erlaubt, einen Formatstring flexibler zu sezieren, zum Beispiel den String an den Platzhalterpositionen zu teilen?

Antwort

2

Da Sie benannte Platzhalter verwenden, würde ich benannte Gruppen verwenden. Dies scheint zu funktionieren:

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) 

und dann zu testen:

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

ich eine andere Weise als mit einer eindeutigen Kennung zu finden gehofft hatte, aber das ist eine interessante Spin auf diesem Ansatz. Insbesondere mag ich, dass ich nur ein einziges eindeutiges Trennzeichen benötige, und nicht nur eines für jedes Feld, das einem anderen regulären Ausdruck entsprechen muss. – miracle2k

+0

Wenn Sie sich mit dem eindeutigen Trennzeichen zu sehr sorgen, können Sie immer eine Zahl darin einfügen und die Zahl erhöhen, bis Sie etwas erhalten, das nicht in der Zeichenkette enthalten ist. – Duncan

+0

ok, das funktioniert für Strings, wäre es möglich, dies für mehr Konstrukte funktionieren zu lassen? wie Parsen "Knoten% (ID) 03d" zu "Knoten (? P \ d \ d \ d)" –

1

Sie können dies versuchen; Es funktioniert um Ihre Probleme zu entkommen.

Verwandte Themen