Python字典能否使用re.compile作为键?

2024-04-25 22:46:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我们有10-20个字典,遵循以下基本格式,键是要分配的值,值是正则表达式:

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

这些类型的字典已经被证明是非常有用的,因为它允许我们将来自多个管理系统的计算机信息标准化,这样就可以协调信息。在

唯一的问题是太慢了。以下是我们使用此类字典规范化数据的函数:

^{pr2}$

因此,基本上,我们循环遍历字典值(regex),当我们找到匹配项时,我们获取键,这就成为新的标准化值。在

但是,由于我们在字典中循环,我们正在丢失与字典类型哈希表相关的速度。但我们如何克服这个问题呢?我可以简单地交换这些字典中的键和值对吗?但是我的注册表查找函数需要如何改变,它会更快吗?在

另一个字典示例:

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))
    ])),
.
.
.

Tags: re字典serverwindowslinecollectionsxpstandard
3条回答

这些是一些非常可怕的正则表达式。看起来它们都可以归结为一些固定的字符串,有些带有选项和必需的组合,没有一个是按顺序排列的。我的第一个反应是将它们包装在(?P<name>...)部分中,并将它们连接到一个巨大的regex中,之后您可以请求groupdict;鉴于内部没有捕获组(更不用说命名组),您甚至可以使用lastgroup(但您希望颠倒测试顺序,而且不会缩短)。但这会让代码变得更难看。我更喜欢这样一种方法:子字符串不被包装在^.*(?=.*...).*$中,而只是简单地列出;因为它有太多的噪音。在

下面是一个粗略的转换方法,可以提取字符串本身并生成一些匹配的集合:

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

顺便说一句,在cpython2.7.10中,这个名义上的问题的答案是肯定的,re模式对象(SRE_模式)是可散列的,并且可以用作键,尽管这可能是依赖于实现的,因为文档中没有明确指定。虽然我不确定这是否适用于这个问题。在

这是我正在使用的解决方案,至少现在是这样。它利用哈希表的速度来查找以前使用字典查找匹配的项,并在查找新项时形成更快的字典。在

根据数据源的标准化速度,我将提高50%。这有点复杂,但不是一个坏的解决方案。。。似乎提供了最好的两个世界。思想?在

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"

与其在字典中循环使用所需结果作为键,将每个可能结果的表达式作为值,为什么不进行一次正则表达式搜索(因为表达式非常基本),然后在字典中查找结果?在

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'

注意,H06.20匹配.的任何字符。如果您想要一个文字点,请使用H06\.20。在

相关问题 更多 >