在Perl正则表达式中匹配所有不在括号和方括号内的逗号。

3
我正在尝试使用正则表达式匹配所有逗号(后面跟着一个空格), ,这些逗号不在任何括号或方括号内,即逗号不能包含在括号或方括号中。
目标字符串是A, An(hi, world[hello, (hi , world) world]); This, These。在这种情况下,它应该匹配第一个逗号和最后一个逗号(即AAn之间的逗号以及thisthese之间的逗号)。
因此,我可以将A, An(hi, world[hello, (hi , world) world]); This, These分成AAn(hi, world[hello, (hi , world) world]); ThisThese,不会导致括号/方括号不平衡。
为此,仅使用正则表达式似乎很难。是否有其他方法解决这个问题?
我正在使用以下正则表达式: , (?![^()\[\]]*[\)\]]) 但是,这个表达式将匹配其他两个额外的逗号, (第二个和第三个),它们不应该被匹配。
虽然如果匹配以下字符串,它将匹配正确的逗号(分别是第一个):A, An(hi, world)A, An[hi, world] 但是,如果括号和方括号相互包含,将会有问题。
更多详细信息请参见此链接: https://regex101.com/r/g8DOh6/1

1
必须只使用正则表达式吗?例如,使用Text::Balanced可以提取平衡的括号/方括号和其他内容,然后从“其他内容”中挑选逗号。 - zdim
@zdim 我已经更新了帖子。不一定只用正则表达式。任何方法都可以解决问题。 - jonah_w
好的,谢谢!那么...你想要最终结果是什么?是在“_those_commas”(不包括逗号)之前的单词吗?请看我的回答并告诉我(我会编辑更多)——它解决了问题,但我不知道实际的“_result_”应该是什么! - zdim
所以我可以进行最后一步:将 hello(D,) world 转换为 hello world。然而,本文并不是关于这个最终步骤的。它更多地是为最后一步做准备。 - jonah_w
顺便说一句...使用这个工具(Regexp::Common)可以更容易地达到那个“最终步骤”,因为它恰好匹配那些(...)。你想要吗?这就是我现在回答中的内容... - zdim
显示剩余2条评论
3个回答

4

问题在于识别括号/方括号的“均衡”对。这是一个被广泛认可的问题,有相关的库来解决此类问题。它们可以找到顶层匹配的括号对(...)/[...]和所有在括号内部的内容,以及括号外面的所有其他内容--然后处理“其他内容”。

一种方法是使用Regexp::Common

use warnings;
use strict;
use feature 'say';

use Regexp::Common;

my $str = shift // q{A, t(a,b(c,))u B, C, p(d,)q D,}; 

my @all_parts = split /$RE{balanced}{-parens=>'()[]'}/, $str;

my @no_paren_parts = grep { not /\(.*\) | \[.*\]/x } @all_parts;

say for @no_paren_parts;

使用split的属性,当分隔符模式中的正则表达式捕获时,返回包括分隔符在内的列表。库正则表达式进行捕获,因此我们可以得到分割字符串的部分以及与正则表达式匹配的部分。分隔符包含成对的定界符,而其他项则不能,因此我通过过滤它们来排除这些项。 输出:

A, t
u B, C, p
q D,

括号/方括号术语已经消失,但字符串如何分割在某种程度上是任意的。

上述内容有些“通用”,仅使用库来提取平衡的()/[],以及字符串的所有其他部分,或者,我们可以从字符串中删除这些模式。

$str =~ s/$RE{balanced}{-parens=>'()[]'}//g;

保持与

现在可以简单地按逗号分割
my @terms = split /\s*,\s*/, $str;
say for @terms;

for

A
图 B
C
pq D

这在这种情况下是期望的结果,根据评论中的澄清。

另一个最值得注意的库,在许多方面更为基础,是核心库Text::Balance。请参见Shawn's answer这里,例如此贴这个这个的示例。


一个例子。使用

my $str = q(it, is; surely);

my @terms = split /[,;]/, $str;

如果在数组@terms中找到了it,那么肯定会得到it

my @terms = split /([,;])/, $str;

我们在@terms中获取以下所有内容:it,is;surely


此外,通过构造,它包含正则表达式在偶数索引处匹配的内容。因此,对于所有其他部分,我们可以在奇数索引处获取元素

my @other_than_matched_parts = @all_parts[ grep { not $_ & 1 } 0..$#all_parts ];

对于 A, t(a,b(c,)) B, C, u(d,) D,,输出应为 A, t B, C, u D,. - jonah_w
@jonah_w 好的,现在明白了——逗号仍然保留,只需删除 (...)。所以——输出应该是一个数组,就像答案的第一部分一样?(不是像第二部分那样只有一个字符串?) - zdim
当目标字符串为 [d,]u D, 时,u D 是正确的。但是很多情况下目标字符串可能是 u[d,] D,,在这种情况下,结果将会是错误的。 - jonah_w
@jonah_w 逗号分割可以实现这个功能吗 -- 就像这样:go about, go (a)round --> go about, go round(删除答案的第二部分中的 (...)),然后将 go aboutgo round 按逗号分割(表达式的某些部分是否可以嵌入逗号?) - zdim
1
@jonah_w 请注意,您可以将输入提供给上面的程序,prog.pl“.....”(这就是开头的shift // ...所做的--如果输入字符串包含空格,则确实需要在其周围加上引号)。因此,当我在问题中运行它时,对于第二部分的最终输出是:AAn; ThisThese - zdim
显示剩余8条评论

2

检查逗号,是否在括号/括号中,例如:

[(,),],[abc,(def,[ghi,],),],[(,),]
      ^                    ^

意味着模式必须意识到每个括号/括号何时以平衡的方式打开和关闭,因此不仅仅是例如[([],因为它应该是[([])]
这里是另一种解决方案,虽然不能直接解决您的问题,但可能更接近解决问题:
1.匹配以下任一内容: a.逗号 b.用外部[]()包装的组。请参见正则表达式匹配平衡括号 2.过滤掉1.b
正则表达式模式:
(?:\((?>[^()]|(?R))*\)|\[(?>[^\[\]]|(?R))*\]|,)

在此输入图片描述

对于这个字符串,匹配项如下所示:

A, An(hi, world[hello, (hi , world) world]) and this, is that, for [the, one (in, here, [is not,])] and last,here!
 ^   ^------------------------------------^         ^        ^     ^------------------------------^         ^
  • 因此,它没有捕获括号/括号组中的任何逗号,而是将它们作为整体捕获。现在,你有了外层的逗号。

1

zdim提到的一种方法是使用核心Text::Balanced模块。演示:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use Text::Balanced qw/extract_bracketed/;

my $str = "A, An(hi, world[hello, (hi , world) world]); This, These";
my ($inside, $after, $before) = extract_bracketed $str, '()[]', qr/[^([]*/;

my @tokens = (split(/,/, $before//""), $inside, split(/,/, $after//""));

# Displays
# A  An (hi, world[hello, (hi , world) world]) ; This  These
say join(' ', @tokens);

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