我知道这是一个非常古老的问题,您自己可能不再对解决方案感兴趣,但我发现现有答案具有误导性,并想提供另一个答案。(另一个答案中的最终解决方案非常适合给定的问题 - 您不需要使用单个正则表达式来解决所有问题。但您的答案中指出的问题实际上并不是问题。)
您的想法实际上是正确的,但实施不正确。第一个问题是您正在使用积极的后视,这将确保之前出现过相同的值。你想要的是消极的向后看,(?<!...)
以确保该值没有出现。
然而,主要问题是您的后视内容的顺序是错误的。在您的代码中,您首先拥有通配符.*?
,然后是您想要匹配的东西tag (\k<ID>)
。但是您正在寻找当前位置左侧tag X
的某个位置,这意味着通配符实际上需要排在第二位。在后面看,正则表达式引擎的当前位置(在后面的外部)位于内部模式的末尾。想象一下做。这匹配in但不匹配 in 。(更重要的是,如果一定要走在前面,为什么自己不倒过来呢?(?<=abc)def
def
abcdef
cbadef
.*?
tag (\k<ID>)
(\k<ID> gat)
所以这工作得很好:
(\.\.\. (?<ID>(\d+)))(?<!(?s)tag (\k<ID>).*?)
尽管它的组比您实际需要的多得多。这就足够了:
\.\.\. (?<ID>\d+)(?<!(?s)tag \k<ID>.*?)
在这里测试一下。
至于 Simon 的评论“.NET 正则表达式引擎从左到右解析字符,所以你不能从右到左反向引用。” 我不知道那件事。我还没有看到 regex 引擎的代码,但是从多年试验 .NET 的 regex 风格并将其滥用于各种恶作剧中,我可以说它肯定会像你所期望的那样回溯到所有实际目的。(他链接到的更快的算法只适用于不使用反向引用的模式,结果完全无法区分。)模式从左到右匹配,是的。但是,直到您实际到达模式中的后视后才会处理后视,并且它可以反向引用较早的捕获,即使这些在匹配的后期结束。
对此的两个警告是 a) .NET 具有从右到左的匹配模式,该模式实际上从右到左处理模式,因此捕获必须出现在代码中的反向引用之后,并且 b) 后视使用所述从右到-left 模式(不幸的是,这是未记录的),因此引擎实际上可以通过使用正常的从左到右模式中的后视(或从右到左模式中的前瞻,或内部前瞻)来回编织输入字符串向后看,什么不是,但这可能不是您想在生产代码中使用的东西)。
在考虑反向引用何时有效以及何时无效时,这种处理输入的方向始终很重要。