c - 确定正则表达式的特异性

标签 c regex pcre

给定以下正则表达式:

 - alice@[a-z]+\.[a-z]+
 - [a-z]+@[a-z]+\.[a-z]+
 - .*

字符串 alice@myprovider.com 显然会匹配所有三个正则表达式。在我正在开发的应用程序中,我们只对“最具体”的匹配感兴趣。在这种情况下,这显然是第一个。
不幸的是,似乎没有办法做到这一点。我们正在使用 PCRE,但我没有找到执行此操作的方法,并且在 Internet 上搜索也没有结果。
一种可能的方法是保持正则表达式按特异性降序排序,然后简单地获取第一个匹配项。当然接下来的问题就是如何对正则表达式数组进行排序。将责任交给最终用户以确保数组已排序不是一种选择。 所以我希望你们能在这里帮助我......

谢谢!!

保罗

最佳答案

以下是我根据 Donald Miner 的研究论文开发的这个问题的解决方案,用 Python 实现,适用于应用于 MAC 地址的规则。

基本上,最具体的匹配来自不是任何其他匹配模式的超集的模式。对于特定的问题域,您创建了一系列测试(函数)来比较两个 RE 并返回哪个是超集,或者它们是否正交。这使您可以构建匹配树。对于特定的输入字符串,您遍历根模式并找到任何匹配项。然后遍历它们的子模式。如果在任何时候,正交模式匹配,则会引发错误。

设置

import re

class RegexElement:
    def __init__(self, string,index):
        self.string=string
        self.supersets = []
        self.subsets = []
        self.disjoints = []
        self.intersects = []
        self.maybes = []
        self.precompilation = {}
        self.compiled = re.compile(string,re.IGNORECASE)
        self.index = index

SUPERSET  = object()
SUBSET    = object()
INTERSECT = object()
DISJOINT  = object()
EQUAL     = object()

测试

每个测试采用 2 个字符串(a 和 b)并尝试确定它们之间的关系。如果测试无法确定关系,则返回 None。

SUPERSET 表示 ab 的超集。 b 的所有匹配项都将匹配 a

SUBSET 表示 ba 的超集。

INTERSECT 表示 a 的某些匹配项将匹配 b,但有些不会匹配 b > 将不匹配 a

DISJOINT 表示 a 的匹配项都不会匹配 b

EQUAL 表示 a 的所有匹配项将匹配 b 并且 b 的所有匹配项将匹配 一个

    def equal_test(a, b):  
        if a == b: return EQUAL

图表

  class SubsetGraph(object):
    def __init__(self, tests):
        self.regexps = []
        self.tests = tests
        self._dirty = True
        self._roots = None

    @property
    def roots(self):
        if self._dirty:
            r = self._roots = [i for i in self.regexps if not i.supersets]
            return r
        return self._roots

    def add_regex(self, new_regex):
        roots = self.roots
        new_re = RegexElement(new_regex)
        for element in roots:
            self.process(new_re, element)
        self.regexps.append(new_re)

    def process(self, new_re, element):
        relationship = self.compare(new_re, element)
        if relationship:
            getattr(self, 'add_' + relationship)(new_re, element)

    def add_SUPERSET(self, new_re, element):
        for i in element.subsets:
            i.supersets.add(new_re)
            new_re.subsets.add(i)

        element.supersets.add(new_re)
        new_re.subsets.add(element)

    def add_SUBSET(self, new_re, element):
        for i in element.subsets:
            self.process(new_re, i)

        element.subsets.add(new_re)
        new_re.supersets.add(element)

    def add_DISJOINT(self, new_re, element):
        for i in element.subsets:
            i.disjoints.add(new_re)
            new_re.disjoints.add(i)

        new_re.disjoints.add(element)
        element.disjoints.add(new_re)

    def add_INTERSECT(self, new_re, element):
        for i in element.subsets:
            self.process(new_re, i)

        new_re.intersects.add(element)
        element.intersects.add(new_re)

    def add_EQUAL(self, new_re, element):
        new_re.supersets = element.supersets.copy()
        new_re.subsets = element.subsets.copy()
        new_re.disjoints = element.disjoints.copy()
        new_re.intersects = element.intersects.copy()

    def compare(self, a, b):
        for test in self.tests:
            result = test(a.string, b.string)
            if result:
                return result

    def match(self, text, strict=True):
        matches = set()
        self._match(text, self.roots, matches)
        out = []
        for e in matches:
            for s in e.subsets:
                if s in matches:
                    break
            else:
                out.append(e)
        if strict and len(out) > 1:
            for i in out:
                print(i.string)
            raise Exception("Multiple equally specific matches found for " + text)
        return out

    def _match(self, text, elements, matches):
        new_elements = []
        for element in elements:
            m = element.compiled.match(text)
            if m:
                matches.add(element)
                new_elements.extend(element.subsets)
        if new_elements:
            self._match(text, new_elements, matches)

用法

graph = SubsetGraph([equal_test, test_2, test_3, ...])
graph.add_regex("00:11:22:..:..:..")
graph.add_regex("..(:..){5,5}"
graph.match("00:de:ad:be:ef:00")

完整的可用版本是 here .

关于c - 确定正则表达式的特异性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3611860/

相关文章:

Java 正则表达式查找或替换第一次出现而不丢失尾部文本

php - 替换模式内的所有事件

linux - 加载共享库时出错

ruby - RegExp#match 只返回一个匹配项

c - 使用 read 读取时出现段错误

c - 在数组访问/下标中反转操作数的原因

python - 在每隔一行的开头和结尾添加引号,忽略空行

python - Django URL 正则表达式 "is not a valid regular expression"错误

c - 替换字符串中的字符

c - 输出中的 'y' 在 C 中代表什么?