gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 19:22:45 26 4
gpt4 key购买 nike

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

我通读了Balancing Group Definition ,但解释很难理解,我对我提到的问题仍然很困惑。

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

最佳答案

据我所知,平衡组是 .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 允许重复使用相同的名称,所以我们可以编写一个正则表达式,如
(?<word>\w+)\W+(?<word>\w+)

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

我们发现了两个捕获
0: "foo"
1: "bar"

这使我们甚至可以将表达式的不同部分的内容推送到单个堆栈中。但是,这只是 .NET 能够跟踪此 CaptureCollection 中列出的多个捕获的功能。 .但我说,这个合集是 堆栈 .那么我们可以从中取出东西吗?

输入:平衡组

事实证明我们可以。如果我们使用像 (?<-word>...) 这样的组,然后从堆栈中弹出最后一个捕获 word如果子表达式 ...匹配。因此,如果我们将之前的表达式更改为
(?<word>\w+)\W+(?<-word>\w+)

然后第二组将弹出第一组的捕获,我们将收到一个空 CaptureCollection最后。当然,这个例子没什么用。

但是减号语法还有一个细节:如果堆栈已经为空,则该组失败(无论其子模式如何)。我们可以利用这种行为来计算嵌套级别 - 这就是名称平衡组的来源(以及它变得有趣的地方)。假设我们要匹配括号正确的字符串。我们将每个左括号压入堆栈,并为每个右括号弹出一个捕获。如果我们遇到一个右括号太多,它会尝试弹出一个空堆栈并导致模式失败:
^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*$

所以我们在重复中有三个选择。第一个选择消耗所有不是括号的东西。第二个选择匹配 ( s 同时将它们插入堆栈。第三个选择匹配 ) s 从堆栈中弹出元素时(如果可能!)。

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

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

结局:条件模式

还有一个问题:这并不能确保字符串末尾的堆栈为空(因此 (foo(bar) 将是有效的)。 .NET(以及许多其他风格)还有一种结构可以帮助我们解决这个问题:条件模式。一般语法是
(?(condition)truePattern|falsePattern)

哪里 falsePattern是可选的 - 如果省略,则假情况将始终匹配。条件可以是模式,也可以是捕获组的名称。我将在这里重点讨论后一种情况。如果是捕获组的名称,则 truePattern当且仅当该特定组的捕获堆栈不为空时才使用。也就是说,像 (?(name)yes|no) 这样的条件模式读取“如果 name 匹配并捕获了某些内容(仍在堆栈中),请使用模式 yes 否则使用模式 no”。

所以在我们上面模式的末尾,我们可以添加类似 (?(Open)failPattern) 的内容。如果 Open 会导致整个模式失败-stack 不为​​空。使模式无条件失败的最简单的方法是 (?!) (空的负前瞻)。所以我们有我们的最终模式:
^(?:[^()]|(?<Open>[(])|(?<-Open>[)]))*(?(Open)(?!))$

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

从这里开始,天空就是极限。许多非常复杂的用途是可能的,并且在与其他 .NET-Regex 功能(如可变长度后视)( which I had to learn the hard way myself )结合使用时会出现一些问题。然而,主要问题始终是:使用这些功能时,您的代码是否仍可维护?你需要很好地记录它,并确保所有从事它工作的人也知道这些功能。否则,您可能会更好,只需手动逐个字符地遍历字符串并计算整数中的嵌套级别。

附录: (?<A-B>...) 是怎么回事语法?

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

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

但是 .NET 在这里提供了一个更方便的功能:如果我们使用 (?<A-B>subPattern) ,不仅是从堆栈中弹出捕获 B ,但还有弹出捕获 B 之间的所有内容并且这个当前组被插入堆栈 A .因此,如果我们使用这样的组作为右括号,同时从我们的堆栈中弹出嵌套级别,我们还可以将这对的内容插入另一个堆栈:
^(?:[^()]|(?<Open>[(])|(?<Content-Open>[)]))*(?(Open)(?!))$

Kobi 提供了这个 Live-Demo在他的回答中

因此,将所有这些东西放在一起,我们可以:
  • 记住任意多次捕获
  • 验证嵌套结构
  • 捕获每个嵌套级别

  • 全部在一个正则表达式中。如果这不令人兴奋... ;)

    当我第一次了解它们时,我发现一些有用的资源:
  • http://blog.stevenlevithan.com/archives/balancing-groups
  • MSDN on balancing groups
  • MSDN on conditional patterns
  • http://kobikobi.wordpress.com/tag/balancing-group/ (有点学术性,但有一些有趣的应用)
  • 关于c# - 什么是正则表达式平衡组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17003799/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com