5

可以从字符串中获取嵌套花括号中的所有内容吗?例如:

敏捷的棕色狐狸跳过了懒狗

所以我需要:

  • 快的
  • 超过
  • {跳过}懒惰

在这个序列中更好,从最嵌套。

4

4 回答 4

10

解决方案

下面的正则表达式将允许您获取所有嵌套花括号的内容。请注意,这假设嵌套的花括号是平衡的;否则,很难定义答案应该是什么。

(?=\{((?:[^{}]++|\{(?1)\})++)\})

结果将在捕获组 1 中。

演示

请注意,该顺序与问题中指定的顺序不同。打印出来的顺序是由左大括号出现的顺序定义的{,也就是说最外面的对的内容会先打印出来。

解释

现在先忽略零宽度的正向预测 (?=pattern),让我们关注内部的模式,即:

\{((?:[^{}]++|\{(?1)\})++)\}

2 个大括号之间的部分 -((?:[^{}]++|\{(?1)\})++)将匹配以下任一情况的1 个或多个实例:

  • 一个非空的非花括号字符序列[^{}]++,或
  • 递归匹配由 包围的块{},该块可能包含许多其他非花括号序列或其他块。

仅上面的模式就可以匹配不包含{}我们不需要的文本。因此,我们确保匹配是{}由两端的花括号括起来的块{}\{((?:[^{}]++|\{(?1)\})++)\}.

由于我们想要所有嵌套的花括号内的内容,我们需要防止引擎使用文本。这就是使用零宽度正向预测的地方。

它不是很有效,因为您将重做嵌套大括号的匹配,但我怀疑是否有任何其他使用正则表达式的通用解决方案可以有效地处理它。

普通代码可以一次有效地处理所有事情,如果您将来要扩展您的需求,建议您使用。

于 2013-04-28T12:01:41.237 回答
4

一次不使用正则表达式的简单解决方案:

$str = 'The {quick} brown fox {jumps {over the} lazy} dog';

$result = parseCurlyBrace($str);

echo '<pre>' . print_r($result,true) . '</pre>';

function parseCurlyBrace($str) {

  $length = strlen($str);
  $stack  = array();
  $result = array();

  for($i=0; $i < $length; $i++) {

     if($str[$i] == '{') {
        $stack[] = $i;
     }

     if($str[$i] == '}') {
        $open = array_pop($stack);
        $result[] = substr($str,$open+1, $i-$open-1);
     }
  }

  return $result;
}
于 2013-04-30T08:21:38.593 回答
3

你可以试试这个:

$subject = 'The {quick} brown fox {jumps {over the} lazy} dog';

function nestor($subject) {
    $result = false;    
    preg_match_all('~[^{}]+|\{(?<nested>(?R)*)\}~', $subject, $matches);

    foreach($matches['nested'] as $match) {
        if ($match != "") {
            $result[] = $match;
            $nesty = nestor($match);
            if ($nesty) 
                $result = array_merge($result,$nesty); 
                // $result[]=$nesty; // to preserve the hierarchy
        }
    }
    return $result;
}

print_r(nestor($subject));

这里使用的模式匹配嵌套结构,但不能以大于 1 的深度捕获。这就是为什么nestor 函数递归地应用于每个匹配的原因。

您可以使用 \G 功能探索另一种模式:

$subject = 'The {quick} brown fox {jumps {over the}{ fat} lazy} dog';
$pattern = '~[^{}]++|\G\{(?<nested>(?R)*+)\}~';
preg_match_all($pattern, $subject, $matches/*, PREG_SET_ORDER*/);
print_r($matches);

如果查看结果,您可以轻松确定规则以了解每个元素的级别深度。

于 2013-04-28T02:46:21.620 回答
1

你可以用一种 hacky、丑陋的方式来做到这一点,如下所示:

1) 搜索正则表达式的所有匹配项{([^}]*)}

2)搜索正则表达式的所有匹配项{([^}]*{[^}]*}[^}]*)}(如您所见,这可以动态构造)

3)搜索正则表达式的所有匹配项{([^}]*{[^}]*{[^}]*}[^}]*}[^}]*)}......(保持动态构造它更大,直到没有匹配项)

捕获组,由()外对 s 内的{}s 表示,将允许您仅获取第一个捕获组中的内容,而不是整个正则表达式匹配。

于 2013-04-28T00:01:34.230 回答