php - 在 PHP 中合并正则表达式

标签 php regex abstract-syntax-tree

假设我有以下两个包含正则表达式的字符串。我如何合并它们?更具体地说,我希望将这两个表达式作为替代项。

$a = '# /[a-z] #i';
$b = '/ Moo /x';
$c = preg_magic_coalesce('|', $a, $b);
// Desired result should be equivalent to:
// '/ \/[a-zA-Z] |Moo/'

当然,将此作为字符串操作执行是不切实际的,因为这将涉及解析表达式、构建语法树、合并树,然后输出另一个与树等效的正则表达式。没有这最后一步我就很开心了。不幸的是,PHP 没有 RegExp 类(或者有吗?)。

有什么方法可以实现吗?顺便说一下,还有其他语言提供一种方法吗?这不是很正常的场景吗?可能不会。 :-(

或者,有没有一种方法可以有效地检查两个表达式中的任何一个是否匹配,以及哪个匹配得更早(如果它们在相同的位置匹配,则比赛更长)?这就是我现在正在做的事情。不幸的是,我经常在长字符串上针对两个以上的模式执行此操作。结果是(是的,这绝对是瓶颈)。

编辑:

我应该说得更具体——抱歉。 $a$b变量,它们的内容不在我的控制范围内!否则,我会手动合并它们。因此,我不能对所使用的定界符或正则表达式修饰符做出任何假设。例如,请注意,我的第一个表达式使用了 i 修饰符(忽略大小写),而第二个表达式使用了 x(扩展语法)。因此,我不能只是将两者连接起来,因为第二个表达式忽略大小写并且第一个表达式不使用扩展语法(其中的任何空格都很重要!

最佳答案

我实际上看到了 porneL described一堆这样,但这可以解决大部分问题。它取消了先前子表达式中设置的修饰符(另一个答案遗漏了)并设置了每个子表达式中指定的修饰符。它还处理非斜线定界符(我在这里找不到允许字符的规范,所以我使用了,你可能想进一步缩小范围)。

一个弱点是它不处理表达式中的反向引用。我最担心的是反向引用本身的局限性。我将把它留给读者/提问者作为练习。

// Pass as many expressions as you'd like
function preg_magic_coalesce() {
    $active_modifiers = array();

    $expression = '/(?:';
    $sub_expressions = array();
    foreach(func_get_args() as $arg) {
        // Determine modifiers from sub-expression
        if(preg_match('/^(.)(.*)\1([eimsuxADJSUX]+)$/', $arg, $matches)) {
            $modifiers = preg_split('//', $matches[3]);
            if($modifiers[0] == '') {
                array_shift($modifiers);
            }
            if($modifiers[(count($modifiers) - 1)] == '') {
                array_pop($modifiers);
            }

            $cancel_modifiers = $active_modifiers;
            foreach($cancel_modifiers as $key => $modifier) {
                if(in_array($modifier, $modifiers)) {
                    unset($cancel_modifiers[$key]);
                }
            }
            $active_modifiers = $modifiers;
        } elseif(preg_match('/(.)(.*)\1$/', $arg)) {
            $cancel_modifiers = $active_modifiers;
            $active_modifiers = array();
        }

        // If expression has modifiers, include them in sub-expression
        $sub_modifier = '(?';
        $sub_modifier .= implode('', $active_modifiers);

        // Cancel modifiers from preceding sub-expression
        if(count($cancel_modifiers) > 0) {
            $sub_modifier .= '-' . implode('-', $cancel_modifiers);
        }

        $sub_modifier .= ')';

        $sub_expression = preg_replace('/^(.)(.*)\1[eimsuxADJSUX]*$/', $sub_modifier . '$2', $arg);

        // Properly escape slashes
        $sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);

        $sub_expressions[] = $sub_expression;
    }

    // Join expressions
    $expression .= implode('|', $sub_expressions);

    $expression .= ')/';
    return $expression;
}

编辑:我重写了这个(因为我是强制症)并以:

function preg_magic_coalesce($expressions = array(), $global_modifier = '') {
    if(!preg_match('/^((?:-?[eimsuxADJSUX])+)$/', $global_modifier)) {
        $global_modifier = '';
    }

    $expression = '/(?:';
    $sub_expressions = array();
    foreach($expressions as $sub_expression) {
        $active_modifiers = array();
        // Determine modifiers from sub-expression
        if(preg_match('/^(.)(.*)\1((?:-?[eimsuxADJSUX])+)$/', $sub_expression, $matches)) {
            $active_modifiers = preg_split('/(-?[eimsuxADJSUX])/',
                $matches[3], -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
        }

        // If expression has modifiers, include them in sub-expression
        if(count($active_modifiers) > 0) {
            $replacement = '(?';
            $replacement .= implode('', $active_modifiers);
            $replacement .= ':$2)';
        } else {
            $replacement = '$2';
        }

        $sub_expression = preg_replace('/^(.)(.*)\1(?:(?:-?[eimsuxADJSUX])*)$/',
            $replacement, $sub_expression);

        // Properly escape slashes if another delimiter was used
        $sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);

        $sub_expressions[] = $sub_expression;
    }

    // Join expressions
    $expression .= implode('|', $sub_expressions);

    $expression .= ')/' . $global_modifier;
    return $expression;
}

它现在使用 (?modifiers:sub-expression) 而不是 (?modifiers)sub-expression|(?cancel-modifiers)sub-expression 但是我'我们注意到两者都有一些奇怪的修饰符副作用。例如,在这两种情况下,如果子表达式具有 /u 修饰符,它将无法匹配(但如果您将 'u' 作为第二个参数传递新功能,匹配得很好)。

关于php - 在 PHP 中合并正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/244959/

相关文章:

php - 观察 root 并检查 PHP 中的新文件

python - 将ast.Num转换为decimal.Decimal以获得python中的精度

haskell - 具有 Bound 的相互递归语法

javascript - Javascript 中带有通配符/Min、MaxLength 的字符串正则表达式包含数字

regex - grep regex 前瞻或字符串开始(或后视或字符串结尾)

regex - re.search AND 链接多个条件,python3

functional-programming - 有没有允许句法抽象的非 Lisp 方言?

php - CodeIgniter:多个应用程序,如何共享资源?

Windows 中的 PHP 和符号链接(symbolic link)

javascript - 单击表格内容并使其可编辑