正则表达式:删除方括号中的内容

3
有没有一个正则表达式可以与搜索/替换一起使用,以删除出现在方括号中的所有内容(以及方括号)?
我尝试过`\[.*\]`,但它会吞掉额外的东西(例如`"[chomps] extra [stuff]"`)。
同样,使用懒惰匹配的相同方法`\[.*?\]`当存在嵌套括号时不起作用(例如`"stops [chomping [too] early]!"`)。

1
你在使用什么语言的正则表达式?一般来说,你的问题(递归匹配)只能由一些语言解决(.NET、Perl和其他一些,不是JS,也不是Java)。 - xanatos
这里有一个例子,他们使用<和> http://compgroups.net/comp.lang.perl.misc/FAQ-6.12-Can-I-use-Perl-regular-expressions-to-match-balanced-text,3 您可以轻松地将其转换为匹配[和]。我不会用Perl,所以我不会写答案。我还会添加另一个链接https://dev59.com/gW855IYBdhLWcg3wQx9L - xanatos
我能想到的最好方法是将正则表达式放入while循环中,删除方括号内的内容(没有嵌套),直到没有方括号为止。Perl能做得更好吗? - ajwood
是的,请查看我的和Tim的答案。 - Bart Kiers
5个回答

11

试试这样:

$text = "stop [chomping [too] early] here!";
$text =~ s/\[([^\[\]]|(?0))*]//g;
print($text);

这将会打印:

stop  here!

简短的解释:

\[            # match '['
(             # start group 1
  [^\[\]]     #   match any char except '[' and ']'
  |           #   OR
  (?0)        #   recursively match group 0 (the entire pattern!)
)*            # end group 1 and repeat it zero or more times
]             # match ']'

以上正则表达式将被替换为空字符串。

您可以在网上测试它:http://ideone.com/tps8t

编辑

如@ridgerunner所提到的,您可以通过使*和字符类[^\[\]]匹配一次或更多次并使其具有所有权,甚至通过从第1组中制作非捕获组来更有效地创建正则表达式:

\[(?:[^\[\]]++|(?0))*+]

然而,只有在处理大字符串时才能真正提高速度(当然,您可以进行测试!)。


2
+1 而且这个表达式可以通过在第一个选择项中添加一个所有格加号来提高效率:\[([^\[\]]++|(?0))*] - ridgerunner

5
这在正则表达式中从技术上讲是不可能的,因为你要匹配的语言不符合“正则”的定义。但是有一些扩展的正则表达式实现可以使用递归表达式来实现,其中包括:

Greta:

http://easyethical.org/opensource/spider/regexp%20c++/greta2.htm#_Toc39890907

PCRE

http://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions

请参考“递归模式”,其中有一个括号的示例。
PCRE递归括号匹配看起来像这样:
\[(?R)*\]

编辑:

既然您提到使用Perl,这里有一篇专门介绍如何在Perl中匹配平衡的操作符对的页面:

http://perldoc.perl.org/perlfaq6.html#Can-I-use-Perl-regular-expressions-to-match-balanced-text%3f

类似于:

$string =~ m/(\[(?:[^\[\]]++|(?1))*\])/xg;

4

由于您正在使用Perl,因此可以使用来自CPAN的模块,而不必编写自己的正则表达式。请查看Text::Balanced模块,该模块允许您从平衡的定界符中提取文本。使用此模块意味着如果您的定界符突然更改为{},您不必想办法修改复杂的正则表达式,您只需要在一个函数调用中更改定界符参数即可。


3

如果你只关心删除内容而不是捕获它们以便在其他地方使用,那么你可以使用从嵌套组的内部到外部重复移除的方法。

my $string = "stops [chomping [too] early]!";
# remove any [...] sequence that doesn't contain a [...] inside it
# and keep doing it until there are no [...] sequences to remove
1 while $string =~ s/\[[^\[\]]*\]//g; 
print $string;
< p > 1 while 会在条件为真时什么都不做。如果一个 s/// 匹配并删除一个括号内的部分,则循环将被重复执行,并且 s/// 将再次运行。

即使您使用较旧版本的Perl或其他不支持Bart Kiers答案中的 (?0) 递归扩展模式的语言,这也有效。


1

你想要删除只有在[]之间但不是[]本身的内容。例如:

\[[^\]]*\]

这是一个相当混乱的[]s;-)

它无法处理多个嵌套的[]s。也就是说,匹配[foo [bar] baz]将不起作用。


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