2017-01-23 3 views
1

Wir 10-20 Wörterbücher haben, der diese grundlegende Format folgen, ist der Schlüssel der Wert zugewiesen werden und der Wert ist die Regex:Kann ein Python-Wörterbuch re.compile als Schlüssel verwenden?

osTypeRE = collections.OrderedDict([ 
    ('WINDOWS', re.compile('^.*(windows|WIN2008|WIN2003).*$', re.MULTILINE+re.IGNORECASE)), 
    ('LINUX/UNIX', re.compile('^.*(unix|linux|ubuntu|red hat|redhat|RHEL|CentOS|CENT OS|Debian|SLES|SUSE|freebsd|free bsd|AIX|Solaris|SunOS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('MAC', re.compile('^.*(mac os x).*$', re.MULTILINE+re.IGNORECASE)), 
    ('STRATUS VOS', re.compile('^.*(VOS|Stratus).*$', re.MULTILINE+re.IGNORECASE)), 
    ('MAINFRAME', re.compile('^.*(OS400|AS400).*$', re.MULTILINE+re.IGNORECASE)), 
    ('CISCO IOS', re.compile('^.*(IOS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('NETWARE/OES', re.compile('^.*(NETWARE|OES|Open Enterprise Server).*$', re.MULTILINE+re.IGNORECASE)), 
    ('OPENVMS', re.compile('^.*(VMS).*$', re.MULTILINE+re.IGNORECASE)), 
    ('HYPERVISOR', re.compile('^.*(ESX).*$', re.MULTILINE+re.IGNORECASE)), 
    ('HP NONSTOP', re.compile('^.*(NONSTOP|Tandem|NON STOP).*|.*(H06.20).*$', re.MULTILINE+re.IGNORECASE)), 
    ('EMBEDDED', re.compile('^.*(QNX).*$', re.MULTILINE+re.IGNORECASE)) 
]) 

haben diese Art von Wörterbücher erwiesen sich als sehr nützlich, da es uns erlaubt zu standardisieren Computerinformationen aus mehreren Managementsystemen, so dass die Informationen dann abgestimmt werden können.

Das einzige Problem ist, dass es zu langsam ist. Hier ist die Funktion, die wir pflegen unsere Daten mit dieser Art von Wörterbuch zu normalisieren:

def reg_lookup(lookup, re_dict): 
    value = "INDETERMINATE" 
    for key, reg_ex in re_dict.items(): 
     matched = reg_ex.search(lookup) 
     if matched: 
      value = key.upper() 
      break 
    return value 

Also im Grunde, wir Schleife durch die Wörterbuch-Werte (die reguläre Ausdrücke), und wenn eine Übereinstimmung gefunden nehmen wir den Schlüssel und das wird der neue standardisierte Wert.

Aber da wir durch das Wörterbuch loopen, verlieren wir die Geschwindigkeit, die mit der Dictionary-Typ-Hash-Tabelle verbunden ist. Aber wie würden wir das überwinden? Kann ich die Schlüssel-Wert-Paare in diesen Wörterbüchern einfach austauschen? Aber wie sollte meine reg_lookup-Funktion geändert werden und wäre sie schneller?

Ein weiteres Beispiel Wörterbuch:

osVerRE = collections.OrderedDict([ 
    ('WINDOWS', collections.OrderedDict([ 
     ('WINDOWS SERVER 2000 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2000).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER|.* SR)(?=.*2003)(?=.*ENTERPRISE|.* Ent).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003).*|(?=.*WIN2003).*|(?=.*Windows)(?=.*SERVER)(?=.*2k3).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*ENTERPRISE|.* ENT).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8)(?=.*ENTERPRISE|.* ENT).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*STANDARD|.*STD).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 DATACENTER', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008)(?=.*DATACENTER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2008 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2008).*|(?=.*WINDOWS)(?=.*SERVER)(?=.*2K8).*|(?=.*WIN2008).*|(?=.*WINDOWS 2K8 R2).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 DATACENTER', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012)(?=.*DATACENTER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012)(?=.*STANDARD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2012 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2012).*|(?=.*WINDOWS)(?=.*2012 R2).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS NT 4.0', re.compile('^(?=.*WINDOWS)(?=.*NT)(?=.*4\.0).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS XP PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*XP)(?=.*PROFESSIONAL).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 10 PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*10)(?=.*PRO).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*ENTERPRISE).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 PROFESSIONAL', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*PROFESSIONAL).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 ULTIMATE', re.compile('^(?=.*WINDOWS)(?=.*7)(?=.*ULTIMATE).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER LINE', re.compile('^(?=.*WINDOWS)(?=.*SERVER).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS XP LINE', re.compile('^(?=.*WINDOWS)(?=.*XP).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 7 LINE', re.compile('^(?=.*WINDOWS)(?=.*7).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 8 LINE', re.compile('^(?=.*WINDOWS)(?=.*8).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS 10 LINE', re.compile('^(?=.*WINDOWS)(?=.*10).*$', re.MULTILINE+re.IGNORECASE)) 
    ])), 
. 
. 
. 
+0

Nun, wenn Sie Schleife haben müssen Sie Schleife ... Warum ein Wörterbuch an erster Stelle? –

+0

Wenn Sie jeden Ausdruck für eine Übereinstimmung überprüfen müssen, welchen Unterschied macht es? – TigerhawkT3

+0

@ juanpa.arrivillaga: Sie müssen nicht loopen, da ein Lexer auch diese Aufgaben erledigt und in der Länge des zu parsenden Inhalts arbeitet, nicht die Menge potenzieller Token. –

Antwort

0

Statt mit dem gewünschten Ergebnis als Schlüssel und jedes mögliche Ergebnis des Ausdrucks als Wert durch ein Wörterbuch Looping, warum nicht eine Regex-Suche (da Ihre Ausdrücke sind sehr einfach) und dann das Ergebnis in einem Wörterbuch nachschlagen?

def reg_lookup(lookup, expression=re.compile('windows|WIN2008|WIN2003|unix|linux|ubuntu|red hat|redhat|RHEL|CentOS|CENT OS|Debian|SLES|SUSE|freebsd|free bsd|AIX|Solaris|SunOS|mac os x|VOS|Stratus|OS400|AS400|IOS|NETWARE|OES|Open Enterprise Server|VMS|ESX|NONSTOP|Tandem|NON STOP|H06.20|QNX', re.MULTILINE|re.IGNORECASE), dct={'windows':'WINDOWS', 'WIN2008':'WINDOWS', 'WIN2003':'WINDOWS', 'unix':'LINUX/UNIX', 'linux':'LINUX/UNIX'}): 
    result = expression.search(lookup) 
    if result: 
     return dct[result.group()] 
    else: 
     return 'INDETERMINATE' 

Beachten Sie, dass H06.20 für die . alle Zeichen übereinstimmt. Wenn Sie einen Literalpunkt wünschen, verwenden Sie H06\.20.

+0

Sie sollten auch bitweise - oder das flags Argument - 're.MULTILINE | re.IGNORECASE' statt 're.MULTILINE + re.IGNORECASE' –

+0

Gute Antwort, die einzige Sache ist, dass die Regex mit diesem Ansatz völlig überflüssig wird. für alle 250k-Computer-Assets könnte ich einfach ein Wörterbuch mit jeder beobachteten Variation als Schlüssel und dem standardisierten Wert als Wert erstellen. Dies würde die Hash-Tabelle-Geschwindigkeit voll ausnutzen, aber ich fürchte, es wäre nicht so flexibel und dynamisch in der Natur. Danke für . Korrektur. – gunslingor

+0

@ JonClements - Mein Fehler; Danke. – TigerhawkT3

0

Hier ist die Lösung, die ich verwende, zumindest für jetzt. Es nutzt die Geschwindigkeit der Hash-Tabelle für Elemente, die zuvor mithilfe der Dictionary-Lookups abgeglichen wurden, und bildet das schnellere Wörterbuch während der Suche nach neuen Elementen.

Ich habe eine Geschwindigkeitszunahme von 50% bis 75%, abhängig von der standardisierten Datenquelle. Es ist eine etwas verworrene, aber keine schlechte Lösung ... scheint das Beste aus beiden Welten zu bieten. Gedanken?

fast_lookup = {"mfr":{}, "model":{}, "dev_type":{}, "os_type":{}, "os_ver":{}, "country":{}, "state":{}, } 

def reg_lookup(lookup, re_dict, dict_name): 
    global fast_lookup 
    try: 
     #First try the faster dynamically generated dictionary 
     return fast_lookup[dict_name][lookup] 
    except: 
     #Otherwise, use the lookup dictionaries 
     for key, reg_ex in re_dict.items(): 
      matched = reg_ex.search(lookup) 
      if matched: 
       #Should a match be found, add it to the faster dictionary 
       fast_lookup[dict_name][lookup] = key.upper() 
       return key.upper() 
     return "INDETERMINATE" 
0

Das sind einige ziemlich schreckliche Regexes. Es sieht so aus, als würden sie alle auf eine Handvoll fester Strings reduziert, einige mit Optionen und erforderlichen Kombinationen, keine mit der Reihenfolge. Meine erste Reaktion war, sie in (?P<name>...) Abschnitte zu wickeln und sie zu einem riesigen Regex zu verbinden, nach dem Sie den Gruppendikt anfordern konnten; Da es keine einfangenden Gruppen gibt (geschweige denn benannte), könnten Sie sogar die letzte Gruppe verwenden (aber Sie möchten, dass die Reihenfolge der Tests umgekehrt wird und die Verknüpfung nicht abkürzt). Aber das wird den Code noch hässlicher machen. Ich würde einen Ansatz bevorzugen, bei dem die Teilstrings nicht in ^.*(?=.*...).*$ verpackt sind, sondern einfach aufgelistet werden; Es ist zu viel Lärm wie es ist.

Hier ist eine grobe Umwandlungsverfahren die Saiten selbst zu extrahieren, und einige anpassungsfähigen Mengen zu produzieren:

import re, itertools 

l=[('WINDOWS SERVER 2000 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2000).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 ENTERPRISE', re.compile('^(?=.*WINDOWS)(?=.*SERVER|.* SR)(?=.*2003)(?=.*ENTERPRISE|.* Ent).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 STANDARD', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003)(?=.*STANDARD|.*STD).*$', re.MULTILINE+re.IGNORECASE)), 
     ('WINDOWS SERVER 2003 SERIES', re.compile('^(?=.*WINDOWS)(?=.*SERVER)(?=.*2003).*|(?=.*WIN2003).*|(?=.*Windows)(?=.*SERVER)(?=.*2k3).*$', re.MULTILINE+re.IGNORECASE)),] 

def re2wordsets(e): 
    # Extracts the words, and converts alternatives into multiple sets. 
    groups=map(str.lower,re.findall(r'\(\?=\.\*([^)]*)\)', e.pattern)) 
    groups=[g.split('|.*') for g in groups] 
    for words in itertools.product(*groups): 
     yield set(words) 

# Convert to set form 
l2 = [(k,list(re2wordsets(e))) for (k,e) in l] 
# Collect all words that are used 
allwords = reduce(set.union, itertools.chain(*(i[1] for i in l2))) 
# Build a search regex to find any of them 
allwordsre = re.compile('|'.join(allwords), re.IGNORECASE | re.MULTILINE) 

# Use that regex once to find all relevant words in some string 
foundwords = set(map(str.lower, allwordsre.findall(somestring))) 
# Then see if we find a matching set 
# Note: this could be simplified if we flatten the list of sets 
# e.g. [(k,v1), (k,v2)] instead of [(k,[v1,v2])] 
for k,vs in l2: 
    for v in vs: 
     if v.issubset(foundwords): 
      print k 
      break 
    else:  # none of our sets matched, check next entry 
     continue 
    break 

By the way, die Antwort auf die Titelfrage ist ja in CPython 2.7.10, Objekte neu Muster (SRE_Pattern) sind hashbar und können als Schlüssel verwendet werden, obwohl dies abhängig von der Implementierung sein kann, da es nicht explizit in der Dokumentation angegeben ist. Obwohl ich mir nicht sicher bin, ob das wirklich für das Problem gilt.

+0

Der Hauptgrund für die Frage ist, die Dinge zu beschleunigen. So wie es jetzt ist, muss ich die Wörterbuchwerte durchlaufen und die Regex-Vergleiche nacheinander durchführen. Nimmt ungefähr 50 Sekunden für 150.000 Datensätze und ich möchte etwas schneller. also dachte ich daran, den Schlüssel und den Wert umzukehren ... aber ich weiß nicht wirklich, wie das funktionieren würde. – gunslingor

Verwandte Themen