使用PHP正则表达式检测括号内的文本,忽略嵌套括号

9
我是一个有用的助手,可以为您进行文本翻译。以下是您需要翻译的内容:

我正在尝试编写一个PHP正则表达式来解析带有括号的字符串,同时忽略可能存在的嵌套括号:

比如说,我想要

Lorem ipsum [1. dolor sit amet, [consectetuer adipiscing] elit.]. Aenean commodo ligula eget dolor.[2. Dolor, [consectetuer adipiscing] elit.] Aenean massa[3. Lorem ipsum] dolor.

返回

[1] => "dolor sit amet, [consectetuer adipiscing] elit."
[2] => "Dolor, [consectetuer adipiscing] elit."
[3] => "Lorem ipsum"

到目前为止,我已经掌握了:

'/\[([0-9]+)\.\s([^\]]+)\]/gi'

但是当出现嵌套括号时,它就会失效。 查看演示

如何忽略内部括号的检测? 提前致谢!


由于嵌套结构的存在,我认为正则表达式并不适用于此种情况。也许一个简单的例程是更好的方法。 - someOne
3个回答

5

您可以使用递归引用到之前的组:

(?<no_brackets>[^\[\]]*){0}(?<balanced_brackets>\[\g<no_brackets>\]|\[(?:\g<no_brackets>\g<balanced_brackets>\g<no_brackets>)*\])

查看实例

这个方法的想法是,用[]包裹无括号内容或包含一系列无括号或平衡括号的内容来定义所需匹配项。


我之前不知道有命名捕获组,非常有帮助! - hm711

2
您可以使用这个模式来将项目编号和后面的文本分别捕获到两个不同的组中。如果您确定所有的项目编号都是唯一的,那么您可以使用简单的 array_combine 来构建在您的问题中描述的关联数组:
$pattern = '~\[ (?:(\d+)\.\s)? ( [^][]*+ (?:(?R) [^][]*)*+ ) ]~x';

if (preg_match_all($pattern, $text, $matches))
    $result =  array_combine($matches[1], $matches[2]);

模式细节:

~     # pattern delimiter
\[    # literal opening square bracket
(?:(\d+)\.\s)? # optional item number (*) 
(              # capture group 2
   [^][]*+         # all that is not a square bracket (possessive quantifier)
   (?:             # 
       (?R)        # recursion: (?R) is an alias for the whole pattern
       [^][]*      # all that is not a square bracket
   )*+             # repeat zero or more times (possessive quantifier)
)
]                  # literal closing square bracket
~x  # free spacing mode

请注意,如果您想使用递归与(?R),则必须将项目编号部分设置为可选项(例如:[consectetuer adipiscing]没有项目编号。)。如果您想避免不带项目编号的方括号,则可能会出现问题。在这种情况下,您可以将可选组(?:(\d+)\.\s)?更改为条件语句以构建更强大的模式:(?(R)|(\d+)\.\s) 条件语句:
(?(R)        # IF you are in a recursion
             # THEN match this (nothing in our case)
  |          # ELSE
  (\d+)\.\s  #   
)

这样,项目编号就变得强制性了。


1
你可以使用递归正则表达式获取所有用方括号括起来的子字符串,然后在 array_map 中使用 preg_replace 去除方括号和包围方括号的字符:
$str = "Lorem ipsum [1. dolor sit amet, [consectetuer adipiscing] elit.]. Aenean commodo ligula eget dolor.[2. Dolor, [consectetuer adipiscing] elit.] Aenean massa[3. Lorem ipsum] dolor.";
preg_match_all('/\[(?>[^\[\]]|(?R))*]/', $str, $matches);
$res = array_map(function($el) {
    return preg_replace('/^\[\d+\.(.*?)\s*\]$/s', '$1', $el); 
    },
    $matches[0]);
print_r($res);

请参见IDEONE演示

\[(?>[^\[\]]|(?R))*]正则表达式匹配[,然后是除了[]或嵌套的[...]构造之外的任何内容。有关在正则表达式中使用递归的更多信息,请访问regular-expressions.info。这里是 regex演示

preg_replace 中的正则表达式 - ^\[\d+\.(.*?)\s*\]$ - 将匹配初始的 [,后跟 1 个或多个数字和一个句点,并匹配并捕获其余部分直到最终可选的空格 (\s*) 和闭合 ]$ 确保括号在字符串末尾匹配)。使用 $1 我们可以恢复其余部分的字符串并用它来填充一个新数组。请参见 第二个正则表达式演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接