PHP正则表达式模式匹配递归

时间:2017-07-05 15:24:14

标签: php regex pcre

我在一个函数中有这个函数,它应该用括号中的括号替换任何括号序列,就像(abc)变成abc一样,它甚至可以递归出现,因为parens可以嵌套。

$return =   preg_replace_callback(
    '|(\((.+)\))+|',
    function ($matches) {
        return $matches[2];
    },
    $s
);

当上述正则表达式以此字符串"a(bcdefghijkl(mno)p)q"作为输入时,它返回"ap)onm(lkjihgfedcbq"。这表明正则表达式匹配一次。我该怎么做才能使它在已经制作的比赛中继续匹配并产生这个'abcdefghijklmnopq'“

3 个答案:

答案 0 :(得分:2)

要匹配平衡的括号子串,您可以在\((?:[^()]++|(?R))*\)方法中使用众所周知的Matching Balanced Constructs模式(在preg_replace_callback中描述),其中匹配值可以进一步操作(仅从匹配中删除所有()符号即使没有正则表达式也很容易做到:

$re = '/\((?:[^()]++|(?R))*\)/';
$str = 'a(bcdefghijkl(mno)p)q((('; // Added three ( at the end
$result = preg_replace_callback($re, function($m) {
    return str_replace(array('(',')'), '', $m[0]);
}, $str);
echo $result; // => abcdefghijklmnopq(((

请参阅PHP demo

要获得重叠匹配,您需要使用已知技术,捕获正向前方,但您将无法一次执行两个操作(替换和匹配),您可以先运行匹配,然后替换:

$re = '/(?=(\((?:[^()]++|(?1))*\)))/';
$str = 'a(bcdefghijkl(mno)p)q(((';
preg_match_all($re, $str, $m);
print_r($m[1]);
// => Array ( [0] => (bcdefghijkl(mno)p)  [1] => (mno) )

请参阅PHP demo

答案 1 :(得分:1)

试试这个,

(

https://regex101.com/r/NsQSla/1

它会匹配) (abc) (abc (abc)) 内的所有内容,只要它们是匹配的对。

实施例

Match 1
   Full match   0-5 `(abc)`
Match 2
   Full match   6-17    `(abc (abc))`

将有以下匹配

{{1}}

答案 2 :(得分:1)

稍微不清楚算法的后置条件应该是什么。在我看来,你想要删除匹配的( )对。这里的假设是,不匹配的括号不会被留下(否则你只需删除所有()' s。

所以我猜这意味着输入字符串a(bcdefghijkl(mno)p)q变为abcdefghijklmnopq但输入字符串a(bcdefghijkl(mno)pq变为a(bcdefghijklmnopq。同样,输入字符串(a))将变为a)

可以使用pcre执行此操作,因为它 提供了一些非常规功能,但我对此表示怀疑。输入字符串的语言不规则;它没有上下文。 @ArtisticPhoenix的答案是匹配完整的对匹配的括号。它不做的是匹配所有嵌套对。在我对语言理论的谦逊理解中,这种嵌套匹配本质上是不规则的。

我建议编写一个解析器去除匹配的括号对。有一点罗嗦,不得不考虑无法匹配的作品:

<?php

// Parse the punctuator sub-expression (i.e. anything within ( ... ) ).
function parse_punc(array $tokens,&$iter) {
    if (!isset($tokens[$iter])) {
        return;
    }

    $inner = parse_punc_seq($tokens,$iter);
    if (!isset($tokens[$iter]) || $tokens[$iter] != ')') {
        // Leave unmatched open parentheses alone.
        $inner = "($inner";
    }

    $iter += 1;
    return $inner;
}

// Parse a sequence (inside punctuators).
function parse_punc_seq(array $tokens,&$iter) {
    if (!isset($tokens[$iter])) {
        return;
    }

    $tok = $tokens[$iter];
    if ($tok == ')') {
        return;
    }
    $iter += 1;

    if ($tok == '(') {
        $tok = parse_punc($tokens,$iter);
    }

    $tok .= parse_punc_seq($tokens,$iter);
    return $tok;
}

// Parse a sequence (outside punctuators).
function parse_seq(array $tokens,&$iter) {
    if (!isset($tokens[$iter])) {
        return;
    }

    $tok = $tokens[$iter++];
    if ($tok == '(') {
        $tok = parse_punc($tokens,$iter);
    }

    $tok .= parse_seq($tokens,$iter);
    return $tok;
}

// Wrapper for parser.
function parse(array $tokens) {
    $iter = 0;
    return strval(parse_seq($tokens,$iter));
}

// Grab input from stdin and run it through the parser.
$str = trim(stream_get_contents(STDIN));
$tokens = preg_split('/([\(\)])/',$str,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
var_dump(parse($tokens));

我知道这比一个正则表达式的单行代码要多得多,但它确实解决了我所理解的问题。我有兴趣知道是否有人可以用正则表达式来解决这个问题。