正则表达式负向后看可变距离

时间:2012-08-06 23:37:30

标签: .net regex lookbehind

我正在尝试使用正则表达式在稍后发生的groupB中找到一些在早期groupA中不存在的东西。如果它存在于A中,但不存在于B中,这很好。如果我必须使用正则表达式,这似乎暗示了负面观察的必要性。

大量简化的例子:

文本:

groupA:
tag 789
tag 123

groupB:
Item 1:
... 123
... 456

我很陌生。这就是我们立刻想到的(或十几种变体中的一种),但是你们中间的通知会发现它不能按预期工作。

regex:(\.\.\. (?<ID>(\d+)))(?<=(?s).*?tag (\k<ID>))

我理想的目标是匹配groupB中不存在于groupA中的项目,我无法对输入重新排序。正确的示例输出:(不是由提供的正则表达式完成)

... 456

.NET支持变量回溯距离,但显然我错过了一些东西!

2 个答案:

答案 0 :(得分:2)

.NET正则表达式引擎从左到右解析字符,因此您无法从右向左反向引用。当你到达B组时,A组中的所有角色都已被消耗掉了。无法与之匹敌。虽然正则表达式似乎可以回溯,但它实际上是prematching or branching - 解析器永远不会倒退。

使用正向运行的解析器,您需要首先匹配(并保留)组A中的项目,然后只返回组B中的项目(如果它们不在组A中)。这对于{{ {3}}计算,因为它不能在恒定的空间中完成。

可以通过反转你的字符串并向后运行你的匹配来使用正则表达式来解决你的问题,但代码可能会变得非常难以理解:

321 ...
:1 metI
:Bpuorg

321 gat
987 gat
:Apuorg

"((?<id>\d+)(?=\s\.\.\.))(?!.*\k<id>\sgat)"

结果:

"654"

相反,我建议用这样的东西保持简单:

var groupA = Regex.Matches(text, @"(?<=tag\s)\d+").Cast<Match>().Select(x => x.Value);
var groupB = Regex.Matches(text, @"(?<=\.\.\.\s)\d+").Cast<Match>().Select(x => x.Value);

var bNotA = groupB.Except(groupA);

答案 1 :(得分:0)

我知道这是一个非常古老的问题,你可能不再对解决方案感兴趣,但我发现现有的答案会产生误导,并希望提供另一个解决方案。 (另一个答案中的最终解决方案对于给定的问题非常有用 - 您不需要使用单个正则表达式来解决所有问题。但是您的答案中指出的问题实际上并不是问题。)

你的想法其实是正确的,但实施不是。第一个问题是你正在使用一个积极的lookbehind,这将确保之前出现相同的值 。你想要的是消极的后视,(?<!...)以确保没有的值出现。

然而,主要的问题是你的lookbehind内容的顺序是错误的。在您的代码中,您首先拥有通配符.*?然后您要匹配的内容tag (\k<ID>)。但是你正在寻找当前位置的 left 某处的tag X,这意味着通配符实际上需要排在第二位。在后面后面,正则表达式引擎的当前位置(在lookbehind之外)位于模式内部的 end 。想象一下(?<=abc)def。这与def中的abcdef匹配,但与cbadef中的.*?不匹配。 (更重要的是,如果tag (\k<ID>)必须放在前面,为什么(\k<ID> gat)本身不会像(\.\.\. (?<ID>(\d+)))(?<!(?s)tag (\k<ID>).*?) 那样被颠倒?

所以这很好用:

\.\.\. (?<ID>\d+)(?<!(?s)tag \k<ID>.*?)

虽然它有比你实际需要的更多的群体。这就足够了:

{{1}}

Test it here.

至于Simon的评论“.NET正则表达式引擎从左到右解析字符,所以你不能从右到左反向引用。”我不知道。我没有看到正则表达式引擎的代码,但是多年来尝试使用.NET的正则表达式并且滥用它来处理各种各样的恶作剧,我可以说它肯定出现来回溯你所期望的所有实际目的。 (他链接到的更快的算法仅适用于不使用反向引用的模式,其中结果完全无法区分。)模式从左到右匹配,是的。但是直到你实际到达模式中的lookbehind才会处理lookbehind,并且它可以反向引用早期的捕获,即使这些捕获最终> 在匹配中。

对此的两个警告是:a).NET具有从右到左实际处理模式的从右到左匹配模式,因此捕获必须在之后出现在他们的反向引用中代码和b)lookbehinds使用从右到左的模式(遗憾的是没有记录),因此引擎可以通过使用正常的从左到右模式(或侧面的前瞻)的lookbehinds来实际在输入字符串中来回编织从右到左的模式,或者在lookbehinds中的前瞻和什么不是,但这可能不是你想在生产代码中使用的东西。)

当考虑反向引用何时有效以及何时无效时,处理输入的这个方向总是很重要。