java正则平衡组_什么是正则表达式平衡组?

小编典典

据我所知,平衡组是.NET正则表达式风格所独有的。

除了:重复的组

首先,您需要知道.NET(据我所知)是唯一的正则表达式,可让您访问单个捕获组的多个捕获(不在反向引用中,而是在匹配完成之后)。

为了举例说明,请考虑模式

(.)+

和字符串"abcd"。

在所有其他正则表达式中,捕获组1将仅产生一个结果:(d请注意,完全匹配当然会abcd如预期的那样)。这是因为捕获组的每次新使用都会覆盖先前的捕获。

另一方面,.NET会记住它们。它是一堆的。匹配上面的正则表达式之后

Match m = new Regex(@"(.)+").Match("abcd");

你会发现

m.Groups[1].Captures

是,CaptureCollection其元素对应于四个捕获

0: "a"

1: "b"

2: "c"

3: "d"

其中的数字是的索引CaptureCollection。因此,基本上每次再次使用该组时,都会将新的捕获推入堆栈。

如果我们使用命名捕获组,它将变得更加有趣。由于.NET允许重复使用相同的名称,因此我们可以编写如下正则表达式

(?\w+)\W+(?\w+)

将两个单词捕获到同一组中。同样,每次遇到具有特定名称的组时,都会将捕获推送到其堆栈中。因此,将此正则表达式应用于输入"foo bar"和检查

m.Groups["word"].Captures

我们发现两个捕获

0: "foo"

1: "bar"

这使我们甚至可以将表达式的不同部分将内容推入单个堆栈中。但是,这只是.NET的功能,它能够跟踪此列表中列出的多个捕获CaptureCollection。但是我说过,这个集合是一个

堆栈 。那么我们可以从中 弹出 东西吗?

输入:平衡组

事实证明我们可以。如果我们使用如的组(?...),则word如果子表达式...匹配,则从堆栈中弹出最后一个捕获。因此,如果我们将之前的表达式更改为

(?\w+)\W+(?\w+)

然后,第二组将弹出第一组的捕获,最后我们将收到一个空白CaptureCollection。当然,这个例子是毫无用处的。

但是,减号语法还有一个细节:如果堆栈已经为空,则该组将失败(无论其子模式如何)。我们可以利用这种行为来计算嵌套级别-

这就是名称平衡组的来源(以及有趣之处)。假设我们要匹配正确括号内的字符串。我们将每个左括号插入堆栈,然后为每个右括号弹出一个捕获。如果遇到太多的右括号,它将尝试弹出一个空堆栈并导致模式失败:

^(?:[^()]|(?[(])|(?[)]))*$

因此,我们在重复中有三种选择。第一种选择消耗所有非括号的内容。第二种选择将(s压入堆栈。第三个替代方案)在从堆栈中弹出元素时匹配s(如果可能!)。

注意: 为澄清起见,我们只检查没有不匹配的括号!这意味着完全不包含括号的字符串 将

匹配,因为它们在语法上仍然有效(在某些语法中,您需要使用括号来匹配)。如果您要确保至少有一组括号,只需在后面添加一个超前(?=.*[(])行^。

但是,这种模式并不完美(或完全正确)。

结局:条件模式

还有一个要注意的地方:这不能确保在字符串末尾堆栈为空(因此(foo(bar)将是有效的)。.NET(以及许多其他版本)还有一个可以帮助我们解决问题的结构:条件模式。通用语法是

(?(condition)truePattern|falsePattern)

其中的falsePattern是可选的-

如果省略,则错误情况将始终匹配。条件可以是模式,也可以是捕获组的名称。我将在这里集中讨论后一种情况。如果它是捕获组的名称,则truePattern仅当该特定组的捕获堆栈不为空时才使用。也就是说,类似的条件模式(?(name)yes|no)为“如果name已匹配并捕获了某些内容(仍在堆栈中,则使用模式,yes否则使用模式no”)。

因此,在上述模式的结尾(?(Open)failPattern),如果Open-stack不为空,我们可以添加类似内容,从而导致整个模式失败。使模式无条件失败的最简单方法是(?!)(空的负向超前)。因此,我们有最终模式:

^(?:[^()]|(?[(])|(?[)]))*(?(Open)(?!))$

请注意,这种条件语法本身与平衡组无关,但是有必要利用它们的全部功能。

从这里开始,天空才是极限。可能有许多非常复杂的用途,并且与其他.NET-

Regex功能(例如变长后视功能)结合使用时,有些陷阱(我必须自己学习硬方法)。但是,主要问题始终是:使用这些功能时,代码是否仍可维护?您需要很好地记录它,并确保使用它的每个人也都知道这些功能。否则,您可能会更好,只需手动逐个字符地移动字符串并以整数形式计算嵌套级别。

附录:(?...)语法是什么?

这部分功劳归Kobi(有关更多详细信息,请参见下面的答案)。

现在,利用以上所有内容,我们可以验证字符串是否已正确括在括号中。但是,如果我们实际上可以获取(嵌套的)所有这些括号内容的捕获,它将有用得多。当然,我们可以记得在未清空的单独捕获堆栈中打开和关闭括号,然后根据它们在单独步骤中的位置进行一些子字符串提取。

但是.NET在这里提供了另一个便利功能:如果使用(?subPattern),不仅从堆栈B弹出捕获,B而且将弹出的捕获与当前组之间的所有内容都压入堆栈A。因此,如果将这样的组用作右括号,则在从堆栈中弹出嵌套级别时,也可以将对的内容压入另一个堆栈中:

^(?:[^()]|(?[(])|(?[)]))*(?(Open)(?!))$

Kobi

在他的回答中提供了此现场演示

因此,将所有这些东西放在一起,我们可以:

任意记住多次捕获

验证嵌套结构

捕获每个嵌套级别

全部在单个正则表达式中。如果那不令人兴奋…;)

当我第一次了解它们时,发现一些有用的资源:

2020-05-19

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值