【问题标题】:Get all nested curly braces获取所有嵌套的花括号
【发布时间】:2013-04-21 21:48:57
【问题描述】:
可以从字符串中获取嵌套花括号中的所有内容吗?例如:
{敏捷}棕色狐狸{跳过}懒惰}狗
所以我需要:
在这个序列中更好,从最嵌套的开始。
【问题讨论】:
标签:
php
regex
preg-match
preg-match-all
【解决方案1】:
解决方案
下面的正则表达式将允许您获取所有嵌套花括号的内容。请注意,这假设嵌套的花括号是平衡的;否则,很难定义答案应该是什么。
(?=\{((?:[^{}]++|\{(?1)\})++)\})
结果将在捕获组 1 中。
DEMO
请注意,该顺序与问题中指定的顺序不同。打印出的顺序是由左大括号{的出现顺序定义的,也就是说最外面的对的内容会先打印出来。
说明
暂时忽略零宽度正向预测 (?=pattern),让我们关注里面的模式,即:
\{((?:[^{}]++|\{(?1)\})++)\}
两个大括号之间的部分 - ((?:[^{}]++|\{(?1)\})++) 将匹配 1 个或更多 个实例:
- 非空非花括号字符序列
[^{}]++,或
- 递归匹配
{} 包围的块,其中可能包含许多其他非花括号序列或其他块。
上面的模式单独可以匹配不包含{}的文本,我们不需要。因此,我们确保匹配是一个由{} 括起来的块,由一对花括号{} 在两端:\{((?:[^{}]++|\{(?1)\})++)\}。
由于我们希望所有嵌套花括号内的内容,我们需要防止引擎消费文本。这就是 零宽度正向预测 发挥作用的地方。
这不是很有效,因为您将重做嵌套括号的匹配,但我怀疑是否有任何其他通用解决方案使用正则表达式可以有效地处理它。
普通代码可以一次有效地处理所有事情,如果您将来要扩展您的需求,建议使用。
【解决方案2】:
一次性使用正则表达式的简单解决方案:
$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;
}
【解决方案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 的深度捕获。
这就是为什么嵌套函数递归地应用于每个匹配的原因。
您可以使用 \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);
如果查看结果,您可以轻松确定规则以了解每个元素的级别深度。
【解决方案4】:
你可以用一种 hacky、丑陋的方式来做,如下所示:
1) 搜索正则表达式{([^}]*)}的所有匹配项
2) 搜索正则表达式{([^}]*{[^}]*}[^}]*)}的所有匹配项(如您所见,这是可以动态构造的)
3) 搜索正则表达式 {([^}]*{[^}]*{[^}]*}[^}]*}[^}]*)}... 的所有匹配项(不断动态构造它,直到找不到匹配项)
捕获组,由 ()s 表示,就在 {}s 的外部对内,将允许您仅捕获第一个捕获组中的内容,而不是整个正则表达式匹配。