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

时间:2022-11-29 20:15:48

I was just reading a question about how to get data inside double curly braces (this question), and then someone brought up balancing groups. I'm still not quite sure what they are and how to use them.

我刚刚读了一个关于如何在双花括号中获取数据的问题(这个问题),然后有人提出了平衡组。我仍然不太确定它们是什么以及如何使用它们。

I read through Balancing Group Definition, but the explanation is hard to follow, and I'm still quite confused on the questions that I mentioned.

我通读了平衡组的定义,但是这个解释很难理解,我仍然对我提到的问题感到困惑。

Could someone simply explain what balancing groups are and how they are useful?

有人能简单地解释一下什么是平衡组以及它们是如何有用的吗?

2 个解决方案

#1


153  

As far as I know, balancing groups are unique to .NET's regex flavor.

据我所知,平衡组是。net的regex风格所独有的。

Aside: Repeated Groups

First, you need to know that .NET is (again, as far as I know) the only regex flavor that lets you access multiple captures of a single capturing group (not in backreferences but after the match has completed).

首先,您需要知道。net是唯一的regex风格,它允许您访问单个捕获组的多个捕获(不是在反向引用中,而是在匹配完成之后)。

To illustrate this with an example, consider the pattern

要用示例来说明这一点,请考虑模式

(.)+

and the string "abcd".

和字符串“abcd”。

in all other regex flavors, capturing group 1 will simply yield one result: d (note, the full match will of course be abcd as expected). This is because every new use of the capturing group overwrites the previous capture.

在所有其他regex口味中,捕获组1只会产生一个结果:d(注意,完全匹配的当然是预期的abcd)。这是因为捕获组的每次新使用都会覆盖以前的捕获。

.NET on the other hand remembers them all. And it does so in a stack. After matching the above regex like

另一方面。net记住了它们。它在堆栈中这样做。在匹配上面的regex之后

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

you will find that

你会发现,

m.Groups[1].Captures

Is a CaptureCollection whose elements correspond to the four captures

captu的元素对应于四个捕获?

0: "a"
1: "b"
2: "c"
3: "d"

where the number is the index into the CaptureCollection. So basically every time the group is used again, a new capture is pushed onto the stack.

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

It gets more interesting if we are using named capturing groups. Because .NET allows repeated use of the same name we could write a regex like

如果我们使用命名捕获组,它会变得更有趣。因为。net允许重复使用相同的名称,所以我们可以像这样编写一个regex

(?<word>\w+)\W+(?<word>\w+)

to capture two words into the same group. Again, every time a group with a certain name is encountered, a capture is pushed onto its stack. So applying this regex to the input "foo bar" and inspecting

将两个单词捕获到同一个组中。同样,每当遇到具有特定名称的组时,都会将捕获推入其堆栈。将这个regex应用到输入“foo bar”并检查

m.Groups["word"].Captures

we find two captures

我们发现两个截图

0: "foo"
1: "bar"

This allows us to even push things onto a single stack from different parts of the expression. But still, this is just .NET's feature of being able to track multiple captures which are listed in this CaptureCollection. But I said, this collection is a stack. So can we pop things from it?

这甚至允许我们将表达式的不同部分的内容推到一个堆栈上。但是,这只是. net的特性,它能够跟踪capturecall中列出的多个捕获。但是我说,这个集合是一个堆栈。那么,我们能从它那里得到一些东西吗?

Enter: Balancing Groups

It turns out we can. If we use a group like (?<-word>...), then the last capture is popped from the stack word if the subexpression ... matches. So if we change our previous expression to

事实证明我们可以。如果我们使用像(?<-word>…)这样的组,那么如果子表达式…匹配。如果我们把之前的表达式改成

(?<word>\w+)\W+(?<-word>\w+)

Then the second group will pop the first group's capture, and we will receive an empty CaptureCollection in the end. Of course, this example is pretty useless.

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

But there's one more detail to the minus-syntax: if the stack is already empty, the group fails (regardless of its subpattern). We can leverage this behavior to count nesting levels - and this is where the name balancing group comes from (and where it gets interesting). Say we want to match strings that are correctly parenthesized. We push each opening parenthesis on the stack, and pop one capture for each closing parenthesis. If we encounter one closing parenthesis too many, it will try to pop an empty stack and cause the pattern to fail:

但是少语法还有一个细节:如果堆栈已经为空,那么组将失败(不管它的子模式是什么)。我们可以利用这种行为来计算嵌套级别——这就是名称平衡组的来源(以及它变得有趣的地方)。假设我们要匹配正确括号括起来的字符串。我们在堆栈上推每个展开括号,并为每个关闭括号取出一个捕获。如果我们遇到一个关闭括号太多,它会尝试弹出一个空堆栈,导致模式失败:

^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*$

So we have three alternatives in a repetition. The first alternative consumes everything that is not a parenthesis. The second alternative matches (s while pushing them onto the stack. The third alternative matches )s while popping elements from the stack (if possible!).

我们有三种选择。第一个选项消耗所有非括号的内容。第二种选择匹配(当把它们推到堆栈上时)。第三种替代方法是,从堆栈中取出元素(如果可能的话)。

Note: Just to clarify, we're only checking that there are no unmatched parentheses! This means that string containing no parentheses at all will match, because they are still syntactically valid (in some syntax where you need your parentheses to match). If you want to ensure at least one set of parentheses, simply add a lookahead (?=.*[(]) right after the ^.

注意:为了澄清,我们只是检查没有不匹配的括号!这意味着根本不包含括号的字符串将匹配,因为它们在语法上仍然有效(在某些语法中,您需要括号来匹配)。如果你想确保至少有一组括号,简单地添加一个超前(? =。*(())^之后。

This pattern is not perfect (or entirely correct) though.

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

Finale: Conditional Patterns

There is one more catch: this does not ensure that the stack is empty at the end of the string (hence (foo(bar) would be valid). .NET (and many other flavors) have one more construct that helps us out here: conditional patterns. The general syntax is

还有一个问题:这并不能确保字符串末尾的堆栈是空的(因此(foo(bar)是有效的)。一般的语法是

(?(condition)truePattern|falsePattern)

where the falsePattern is optional - if it is omitted the false-case will always match. The condition can either be a pattern, or the name of a capturing group. I'll focus on the latter case here. If it's the name of a capturing group, then truePattern is used if and only if the capture stack for that particular group is not empty. That is, a conditional pattern like (?(name)yes|no) reads "if name has matched and captured something (that is still on the stack), use pattern yes otherwise use pattern no".

如果错误模式是可选的——如果省略了它,则错误案例将始终匹配。条件可以是模式,也可以是捕获组的名称。我将在这里集中讨论后一种情况。如果它是捕获组的名称,那么只有当该特定组的捕获堆栈不是空的时候,才使用truePattern。也就是说,一个条件模式(?(name)yes|no)读取“如果名称已经匹配并捕获了一些东西(仍然在堆栈上),使用模式yes否则使用模式no”。

So at the end of our above pattern we could add something like (?(Open)failPattern) which causes the entire pattern to fail, if the Open-stack is not empty. The simplest thing to make the pattern unconditionally fail is (?!) (an empty negative lookahead). So we have our final pattern:

因此,在上面模式的末尾,我们可以添加(?(Open)failPattern)之类的东西,如果开放堆栈不是空的,这会导致整个模式失败。使模式无条件失败的最简单的方法是(?!)所以我们有了最终的模式

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

Note that this conditional syntax has nothing per se to do with balancing groups but it's necessary to harness their full power.

注意,这个条件语法本身与平衡组无关,但有必要充分利用它们的功能。

From here, the sky is the limit. Many very sophisticated uses are possible and there are some gotchas when used in combination with other .NET-Regex features like variable-length lookbehinds (which I had to learn the hard way myself). The main question however is always: is your code still maintainable when using these features? You need to document it really well, and be sure that everyone who works on it is also aware of these features. Otherwise you might be better off, just walking the string manually character-by-character and counting nesting levels in an integer.

从这里开始,天空就是极限。许多非常复杂的用法都是可能的,当与其他. net - regex特性(如可变长度的后视镜)结合使用时,也会遇到一些问题(我自己也必须学会这一点)。但是,主要的问题是:在使用这些特性时,您的代码仍然是可维护的吗?您需要非常好地记录它,并且确保每个在它上工作的人都知道这些特性。否则,您可能会做得更好,只需手动逐个字符地遍历字符串,并在一个整数中计算嵌套级别。

Addendum: What's with the (?<A-B>...) syntax?

Credits for this part go to Kobi (see his answer below for more details).

这部分的学分要归功于Kobi(详见下文他的回答)。

Now with all of the above, we can validate that a string is correctly parenthesized. But it would be a lot more useful, if we could actually get (nested) captures for all those parentheses' contents. Of course, we could remember opening and closing parentheses in a separate capture stack that is not emptied, and then do some substring extraction based on their positions in a separate step.

现在,通过以上所有这些,我们可以验证一个字符串是否被正确地括号括起来。但是,如果我们能够为这些圆括号的内容获取(嵌套的)捕获,那么它将会更加有用。当然,我们可以记住在没有清空的单独捕获堆栈中打开和关闭括号,然后根据它们在单独步骤中的位置进行子字符串提取。

But .NET provides one more convenience feature here: if we use (?<A-B>subPattern), not only is a capture popped from stack B, but also everything between that popped capture of B and this current group is pushed onto stack A. So if we use a group like this for the closing parentheses, while popping nesting levels from our stack, we can also push the pair's content onto another stack:

但. net提供了一个便利功能:如果我们使用(? < a - B >子模式),不仅是一个捕获突然从堆栈B,而且一切突然之间捕捉B和当前集团被压入堆栈答:如果我们使用这样的一群闭括号,而出现嵌套级别从堆栈,我们也可以把一对的内容到另一个堆栈:

^(?:[^()]|(?<Open>[(])|(?<Content-Open>[)]))*(?(Open)(?!))$

Kobi provided this Live-Demo in his answer

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

So taking all of these things together we can:

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

  • Remember arbitrarily many captures
  • 记住任意很多截图
  • Validate nested structures
  • 验证嵌套结构
  • Capture each nesting level
  • 捕捉每个嵌套级别

All in a single regular expression. If that's not exciting... ;)

都在一个正则表达式中。如果这不是令人兴奋的…,)

Some resources that I found helpful when I first learned about them:

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

#2


35  

Just a small addition to M. Buettner's excellent answer:

除了M. Buettner出色的回答之外,我只做了一点小小的补充:

What's the deal with the (?<A-B>) syntax?

(?<A-B>x) is subtly different from (?<-A>(?<B>x)). They result in the same control flow*, but they capture differently.
For example, let's look at a pattern for balanced braces:

(? < a - B > x)巧妙地不同于(? < - >(? < B > x))。它们导致相同的控制流*,但是它们捕获的是不同的。例如,让我们看看平衡括号的模式:

(?:[^{}]|(?<B>{)|(?<-B>}))+(?(B)(?!))

At the end of the match we do have a balanced string, but that is all we have - we don't know where the braces are because the B stack is empty. The hard work the engine did for us is gone.
(example on Regex Storm)

在匹配结束时,我们有一个平衡的字符串,但这就是我们所拥有的——我们不知道大括号在哪里,因为B堆栈是空的。引擎为我们所做的艰苦工作已经一去不复返了。在正则表达式的风暴(示例)

(?<A-B>x) is the solution for that problem. How? It doesn't capture x into $A: it captures the content between the previous capture of B and the current position.

(? x)是这个问题的解。如何?它没有将x捕获到$A中:它捕获了以前捕获的B和当前位置之间的内容。

Let's use it in our pattern:

让我们在我们的模式中使用它:

(?:[^{}]|(?<Open>{)|(?<Content-Open>}))+(?(Open)(?!))

This would capture into $Content the strings between the braces (and their positions), for each pair along the way.
For the string {1 2 {3} {4 5 {6}} 7} there'd be four captures: 3, 6 ,4 5 {6} , and 1 2 {3} {4 5 {6}} 7 - much better than nothing or } } } }.
(example - click the table tab and look at ${Content}, captures)

这将捕获到$Content中大括号之间的字符串(以及它们的位置),每个字符串都对应一个大括号。对于字符串{1 2{3}{4 5{6}}7},将会有4个捕获:3、6、4、5{6}和1 2{3}{4 5{6}}7——比什么都没有或}}}}}强得多。(例如——单击table选项卡,查看${Content}, capture)

In fact, it can be used without balancing at all: (?<A>).(.(?<Content-A>).) captures the first two characters, even though they are separated by groups.
(a lookahead is more commonly used here but it doesn't always scale: it may duplicate your logic.)

事实上,它完全可以使用而不需要平衡:(?).(.(? Content-A>)。(在这里,“前瞻”更常用,但它并不总是可伸缩的:它可能会重复你的逻辑。)

(?<A-B>) is a strong feature - it gives you exact control over your captures. Keep that in mind when you're trying to get more out of your pattern.

(?< a - b >)是一个强大的特性——它可以精确地控制你的捕捉。当你试图从你的模式中得到更多的时候,记住这一点。

#1


153  

As far as I know, balancing groups are unique to .NET's regex flavor.

据我所知,平衡组是。net的regex风格所独有的。

Aside: Repeated Groups

First, you need to know that .NET is (again, as far as I know) the only regex flavor that lets you access multiple captures of a single capturing group (not in backreferences but after the match has completed).

首先,您需要知道。net是唯一的regex风格,它允许您访问单个捕获组的多个捕获(不是在反向引用中,而是在匹配完成之后)。

To illustrate this with an example, consider the pattern

要用示例来说明这一点,请考虑模式

(.)+

and the string "abcd".

和字符串“abcd”。

in all other regex flavors, capturing group 1 will simply yield one result: d (note, the full match will of course be abcd as expected). This is because every new use of the capturing group overwrites the previous capture.

在所有其他regex口味中,捕获组1只会产生一个结果:d(注意,完全匹配的当然是预期的abcd)。这是因为捕获组的每次新使用都会覆盖以前的捕获。

.NET on the other hand remembers them all. And it does so in a stack. After matching the above regex like

另一方面。net记住了它们。它在堆栈中这样做。在匹配上面的regex之后

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

you will find that

你会发现,

m.Groups[1].Captures

Is a CaptureCollection whose elements correspond to the four captures

captu的元素对应于四个捕获?

0: "a"
1: "b"
2: "c"
3: "d"

where the number is the index into the CaptureCollection. So basically every time the group is used again, a new capture is pushed onto the stack.

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

It gets more interesting if we are using named capturing groups. Because .NET allows repeated use of the same name we could write a regex like

如果我们使用命名捕获组,它会变得更有趣。因为。net允许重复使用相同的名称,所以我们可以像这样编写一个regex

(?<word>\w+)\W+(?<word>\w+)

to capture two words into the same group. Again, every time a group with a certain name is encountered, a capture is pushed onto its stack. So applying this regex to the input "foo bar" and inspecting

将两个单词捕获到同一个组中。同样,每当遇到具有特定名称的组时,都会将捕获推入其堆栈。将这个regex应用到输入“foo bar”并检查

m.Groups["word"].Captures

we find two captures

我们发现两个截图

0: "foo"
1: "bar"

This allows us to even push things onto a single stack from different parts of the expression. But still, this is just .NET's feature of being able to track multiple captures which are listed in this CaptureCollection. But I said, this collection is a stack. So can we pop things from it?

这甚至允许我们将表达式的不同部分的内容推到一个堆栈上。但是,这只是. net的特性,它能够跟踪capturecall中列出的多个捕获。但是我说,这个集合是一个堆栈。那么,我们能从它那里得到一些东西吗?

Enter: Balancing Groups

It turns out we can. If we use a group like (?<-word>...), then the last capture is popped from the stack word if the subexpression ... matches. So if we change our previous expression to

事实证明我们可以。如果我们使用像(?<-word>…)这样的组,那么如果子表达式…匹配。如果我们把之前的表达式改成

(?<word>\w+)\W+(?<-word>\w+)

Then the second group will pop the first group's capture, and we will receive an empty CaptureCollection in the end. Of course, this example is pretty useless.

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

But there's one more detail to the minus-syntax: if the stack is already empty, the group fails (regardless of its subpattern). We can leverage this behavior to count nesting levels - and this is where the name balancing group comes from (and where it gets interesting). Say we want to match strings that are correctly parenthesized. We push each opening parenthesis on the stack, and pop one capture for each closing parenthesis. If we encounter one closing parenthesis too many, it will try to pop an empty stack and cause the pattern to fail:

但是少语法还有一个细节:如果堆栈已经为空,那么组将失败(不管它的子模式是什么)。我们可以利用这种行为来计算嵌套级别——这就是名称平衡组的来源(以及它变得有趣的地方)。假设我们要匹配正确括号括起来的字符串。我们在堆栈上推每个展开括号,并为每个关闭括号取出一个捕获。如果我们遇到一个关闭括号太多,它会尝试弹出一个空堆栈,导致模式失败:

^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*$

So we have three alternatives in a repetition. The first alternative consumes everything that is not a parenthesis. The second alternative matches (s while pushing them onto the stack. The third alternative matches )s while popping elements from the stack (if possible!).

我们有三种选择。第一个选项消耗所有非括号的内容。第二种选择匹配(当把它们推到堆栈上时)。第三种替代方法是,从堆栈中取出元素(如果可能的话)。

Note: Just to clarify, we're only checking that there are no unmatched parentheses! This means that string containing no parentheses at all will match, because they are still syntactically valid (in some syntax where you need your parentheses to match). If you want to ensure at least one set of parentheses, simply add a lookahead (?=.*[(]) right after the ^.

注意:为了澄清,我们只是检查没有不匹配的括号!这意味着根本不包含括号的字符串将匹配,因为它们在语法上仍然有效(在某些语法中,您需要括号来匹配)。如果你想确保至少有一组括号,简单地添加一个超前(? =。*(())^之后。

This pattern is not perfect (or entirely correct) though.

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

Finale: Conditional Patterns

There is one more catch: this does not ensure that the stack is empty at the end of the string (hence (foo(bar) would be valid). .NET (and many other flavors) have one more construct that helps us out here: conditional patterns. The general syntax is

还有一个问题:这并不能确保字符串末尾的堆栈是空的(因此(foo(bar)是有效的)。一般的语法是

(?(condition)truePattern|falsePattern)

where the falsePattern is optional - if it is omitted the false-case will always match. The condition can either be a pattern, or the name of a capturing group. I'll focus on the latter case here. If it's the name of a capturing group, then truePattern is used if and only if the capture stack for that particular group is not empty. That is, a conditional pattern like (?(name)yes|no) reads "if name has matched and captured something (that is still on the stack), use pattern yes otherwise use pattern no".

如果错误模式是可选的——如果省略了它,则错误案例将始终匹配。条件可以是模式,也可以是捕获组的名称。我将在这里集中讨论后一种情况。如果它是捕获组的名称,那么只有当该特定组的捕获堆栈不是空的时候,才使用truePattern。也就是说,一个条件模式(?(name)yes|no)读取“如果名称已经匹配并捕获了一些东西(仍然在堆栈上),使用模式yes否则使用模式no”。

So at the end of our above pattern we could add something like (?(Open)failPattern) which causes the entire pattern to fail, if the Open-stack is not empty. The simplest thing to make the pattern unconditionally fail is (?!) (an empty negative lookahead). So we have our final pattern:

因此,在上面模式的末尾,我们可以添加(?(Open)failPattern)之类的东西,如果开放堆栈不是空的,这会导致整个模式失败。使模式无条件失败的最简单的方法是(?!)所以我们有了最终的模式

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

Note that this conditional syntax has nothing per se to do with balancing groups but it's necessary to harness their full power.

注意,这个条件语法本身与平衡组无关,但有必要充分利用它们的功能。

From here, the sky is the limit. Many very sophisticated uses are possible and there are some gotchas when used in combination with other .NET-Regex features like variable-length lookbehinds (which I had to learn the hard way myself). The main question however is always: is your code still maintainable when using these features? You need to document it really well, and be sure that everyone who works on it is also aware of these features. Otherwise you might be better off, just walking the string manually character-by-character and counting nesting levels in an integer.

从这里开始,天空就是极限。许多非常复杂的用法都是可能的,当与其他. net - regex特性(如可变长度的后视镜)结合使用时,也会遇到一些问题(我自己也必须学会这一点)。但是,主要的问题是:在使用这些特性时,您的代码仍然是可维护的吗?您需要非常好地记录它,并且确保每个在它上工作的人都知道这些特性。否则,您可能会做得更好,只需手动逐个字符地遍历字符串,并在一个整数中计算嵌套级别。

Addendum: What's with the (?<A-B>...) syntax?

Credits for this part go to Kobi (see his answer below for more details).

这部分的学分要归功于Kobi(详见下文他的回答)。

Now with all of the above, we can validate that a string is correctly parenthesized. But it would be a lot more useful, if we could actually get (nested) captures for all those parentheses' contents. Of course, we could remember opening and closing parentheses in a separate capture stack that is not emptied, and then do some substring extraction based on their positions in a separate step.

现在,通过以上所有这些,我们可以验证一个字符串是否被正确地括号括起来。但是,如果我们能够为这些圆括号的内容获取(嵌套的)捕获,那么它将会更加有用。当然,我们可以记住在没有清空的单独捕获堆栈中打开和关闭括号,然后根据它们在单独步骤中的位置进行子字符串提取。

But .NET provides one more convenience feature here: if we use (?<A-B>subPattern), not only is a capture popped from stack B, but also everything between that popped capture of B and this current group is pushed onto stack A. So if we use a group like this for the closing parentheses, while popping nesting levels from our stack, we can also push the pair's content onto another stack:

但. net提供了一个便利功能:如果我们使用(? < a - B >子模式),不仅是一个捕获突然从堆栈B,而且一切突然之间捕捉B和当前集团被压入堆栈答:如果我们使用这样的一群闭括号,而出现嵌套级别从堆栈,我们也可以把一对的内容到另一个堆栈:

^(?:[^()]|(?<Open>[(])|(?<Content-Open>[)]))*(?(Open)(?!))$

Kobi provided this Live-Demo in his answer

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

So taking all of these things together we can:

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

  • Remember arbitrarily many captures
  • 记住任意很多截图
  • Validate nested structures
  • 验证嵌套结构
  • Capture each nesting level
  • 捕捉每个嵌套级别

All in a single regular expression. If that's not exciting... ;)

都在一个正则表达式中。如果这不是令人兴奋的…,)

Some resources that I found helpful when I first learned about them:

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

#2


35  

Just a small addition to M. Buettner's excellent answer:

除了M. Buettner出色的回答之外,我只做了一点小小的补充:

What's the deal with the (?<A-B>) syntax?

(?<A-B>x) is subtly different from (?<-A>(?<B>x)). They result in the same control flow*, but they capture differently.
For example, let's look at a pattern for balanced braces:

(? < a - B > x)巧妙地不同于(? < - >(? < B > x))。它们导致相同的控制流*,但是它们捕获的是不同的。例如,让我们看看平衡括号的模式:

(?:[^{}]|(?<B>{)|(?<-B>}))+(?(B)(?!))

At the end of the match we do have a balanced string, but that is all we have - we don't know where the braces are because the B stack is empty. The hard work the engine did for us is gone.
(example on Regex Storm)

在匹配结束时,我们有一个平衡的字符串,但这就是我们所拥有的——我们不知道大括号在哪里,因为B堆栈是空的。引擎为我们所做的艰苦工作已经一去不复返了。在正则表达式的风暴(示例)

(?<A-B>x) is the solution for that problem. How? It doesn't capture x into $A: it captures the content between the previous capture of B and the current position.

(? x)是这个问题的解。如何?它没有将x捕获到$A中:它捕获了以前捕获的B和当前位置之间的内容。

Let's use it in our pattern:

让我们在我们的模式中使用它:

(?:[^{}]|(?<Open>{)|(?<Content-Open>}))+(?(Open)(?!))

This would capture into $Content the strings between the braces (and their positions), for each pair along the way.
For the string {1 2 {3} {4 5 {6}} 7} there'd be four captures: 3, 6 ,4 5 {6} , and 1 2 {3} {4 5 {6}} 7 - much better than nothing or } } } }.
(example - click the table tab and look at ${Content}, captures)

这将捕获到$Content中大括号之间的字符串(以及它们的位置),每个字符串都对应一个大括号。对于字符串{1 2{3}{4 5{6}}7},将会有4个捕获:3、6、4、5{6}和1 2{3}{4 5{6}}7——比什么都没有或}}}}}强得多。(例如——单击table选项卡,查看${Content}, capture)

In fact, it can be used without balancing at all: (?<A>).(.(?<Content-A>).) captures the first two characters, even though they are separated by groups.
(a lookahead is more commonly used here but it doesn't always scale: it may duplicate your logic.)

事实上,它完全可以使用而不需要平衡:(?).(.(? Content-A>)。(在这里,“前瞻”更常用,但它并不总是可伸缩的:它可能会重复你的逻辑。)

(?<A-B>) is a strong feature - it gives you exact control over your captures. Keep that in mind when you're trying to get more out of your pattern.

(?< a - b >)是一个强大的特性——它可以精确地控制你的捕捉。当你试图从你的模式中得到更多的时候,记住这一点。