获取所有嵌套的花括号

时间:2013-04-27 23:58:52

标签: php regex preg-match preg-match-all

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

  

{quick}棕色狐狸{跳过{懒}狗

所以我需要:

  • 快速
  • over the
  • 跳过{懒惰的

从大多数嵌套开始,这个顺序更好。

4 个答案:

答案 0 :(得分:8)

解决方案

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

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

结果将是捕获第1组。

DEMO

请注意,订单不是问题中指定的。打印出的订单由开口大括号{的外观顺序定义,这意味着最外面的一对内容将首先打印出来。

说明

暂时忽略零宽度正面预测 (?=pattern),让我们专注于内部模式,即:

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

2个文字花括号之间的部分 - ((?:[^{}]++|\{(?1)\})++)将匹配 1个或更多个实例:

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

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

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

由于您将重做嵌套大括号的匹配,因此效率不高,但我怀疑还有其他通用解决方案带有正则表达式可以有效地处理它。

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

答案 1 :(得分: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;
}

答案 2 :(得分: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);

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

答案 3 :(得分:1)

你可以用一种黑客,丑陋的方式做到如下:

1)搜索正则表达式{([^}]*)}

的所有匹配项

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

3)搜索正则表达式{([^}]*{[^}]*{[^}]*}[^}]*}[^}]*)}的所有匹配项...(继续动态构建它,直到没有匹配)

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