【问题标题】:PHP regex pattern match recursivePHP正则表达式模式匹配递归
【发布时间】:2017-07-05 15:24:14
【问题描述】:

我在一个函数中有这个,它应该用其中包含的内容替换任何括号序列,例如 (abc) 变为 abc 任何它甚至递归出现的地方,因为括号可以嵌套。

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

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

【问题讨论】:

  • /\((?:[^\(\)]*+|(?0))*\)/这个
  • @ArtisticPhoenix 括号可以出现在主题字符串的任何位置,而不仅仅是在字符串的开头
  • 然后呢?这不依赖于那个。
  • [^的使用表示不是,与^[的使用不同,表示字符串的开始。

标签: php regex pcre


【解决方案1】:

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

$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

【讨论】:

  • 你的模式很接近。但是不匹配内部匹配项,例如使用 a(bcdefghijkl(mno)p)q 馈送它输出 bcdefghijkl(mno)p 并复制粘贴您的代码显示 (mno) 永远不会匹配。
  • 好吧,我的错。我运行错误的模式。还有一件事。如何获得递归产生的匹配项。
  • @CholthiPaulTtiopic:不知道你所说的递归产生的匹配是什么意思。 a(bcdefghijkl(mno)p)q((( 中的唯一匹配是 (bcdefghijkl(mno)p),它在 $m[0] 中。只需将其添加到您可以使用use (&$arr) 传递给匿名方法的数组中。见ideone.com/UdxbWy
  • 我最初真正想做的是匹配平衡括号中的任何内容,例如 a(bcdefghijkl(mno)p)q((( 应该给我 (bcdefghijkl(mno)p)(mno) 匹配 https://regex101.com/r/NsQSla/3
【解决方案2】:

算法的后置条件到底应该是什么还不太清楚。在我看来,您想要删除匹配的 ( ) 对。这里的假设是不匹配的括号被单独留下(否则您只需删除所有 ())。

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

使用pcre 或许可以做到这一点,因为它确实提供了一些非常规的功能,但我对此表示怀疑。输入字符串的语言不规则;它与上下文无关。 @ArtisticPhoenix 的答案是匹配 complete 对匹配的括号。它不匹配所有嵌套对。在我对语言理论的谦虚理解中,这种嵌套匹配本质上是非常规的。

我建议编写一个解析器来去除匹配的括号对。不得不考虑不匹配的作品有点冗长:

<?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));

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

【讨论】:

    【解决方案3】:

    试试这个,

      preg_match('/\((?:[^\(\)]*+|(?0))*\)/', $str )
    

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

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

    例子

    (abc) (abc (abc))
    

    将有以下匹配项

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

    【讨论】:

    • 请告诉我这不能回答问题it appears even recursively because parens can be nested.
    • 请说明它如何回答这部分 ...用括号中的内容替换任何括号序列,例如 (abc) 变为 abc
    • 它解决了他们被卡住的部分......在验证它们是一对匹配的括号后,替换可以像str_replace(['(',')'], '', $match[1]) 一样简单。这真的取决于他们的用例,处理匹配的最佳方式是什么。
    • 那么你需要在你的解决方案中添加这个额外的步骤。
    • 你能把它匹配到已经匹配的匹配中,这样 Match3 = (abc)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 2015-02-17
    • 2012-01-16
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多