Java正则表达式中带有负先行断言的奇异性

Java正则表达式中带有负先行断言的奇异性,java,regex,Java,Regex,我正在努力理解Java中正则表达式的行为,遇到了一些看起来很奇怪的事情。在下面的代码中,测试突然失败,原因我在测试中不理解,消息标签为“6个字母匹配,否定”(随后的两个测试也失败)。我是不是已经盯着这个太久了,还是真的有什么奇怪的事情发生了?我不认为这与可变长度的负前瞻断言(?!X)有关,但我很高兴听到任何理论,甚至确认其他人正在经历相同的问题,并且这不是我的JVM特有的。很抱歉,正则表达式如此做作,但您不想看到真实的东西:) 让我们看看前瞻如何匹配: pe literal,

我正在努力理解Java中正则表达式的行为,遇到了一些看起来很奇怪的事情。在下面的代码中,测试突然失败,原因我在测试中不理解,消息标签为“6个字母匹配,否定”(随后的两个测试也失败)。我是不是已经盯着这个太久了,还是真的有什么奇怪的事情发生了?我不认为这与可变长度的负前瞻断言(?!X)有关,但我很高兴听到任何理论,甚至确认其他人正在经历相同的问题,并且这不是我的JVM特有的。很抱歉,正则表达式如此做作,但您不想看到真实的东西:)


让我们看看前瞻如何匹配:

pe           literal, matches "pe"
r            matches \p{Alpha}{1,100}
s            matches [sx]
因此,负向前看不匹配(字符串的尾部,
“onne sont”
,在这里是不相关的)

如果您认为下一个单词不应以s或x结尾,则将
\\b
放在
[sx]
之后可能会有所帮助。始终记住,消极的前瞻不会因为失败而感到遗憾,也不会为了发现如何使您的regexp不匹配而回溯

UPD:让我们仔细看看案例5,将其与案例6进行比较。在这里,我们使用假设匹配(对于前面的表达式),所以我们必须考虑它能(几乎)发生的几个变体。 如果第二个词是“个性化”,我们将有另一次有趣的冒险

UPD2:评论中的讨论促使我在此处添加一个概括:

正则表达式之所以具有吸引力,是因为它们具有人类思维的一个重要特征:。当我们编写正则表达式时,我们希望它们匹配;即使我们的工作是防止无效输入,我们大多数时候也会考虑有效输入。正则表达式匹配器通常共享此属性:它希望匹配,但讨厌失败。这就是为什么像
\p{Alpha}{1100}
这样的子表达式并不意味着“在尝试匹配其余输入之前,吃掉可用的最长的Alpha块”。这大致意味着,“考虑长度在[1100]以内的所有可能的字母块,找到一种方法使整个表达式匹配”

这就是为什么使用regexp时,很容易忽略复杂表达式的误报:错误接受的无效输入。这个问题没有出现,只是在我们使用负面外观时变得更加明显:


在负前瞻中,regexp matcher希望匹配内部表达式(使外部表达式失败)。人类程序员仍然希望匹配外部表达式;正如我们在示例中看到的,这个因素确实影响我们对内部表达式的推理。我们认为它不应该如此努力地匹配(例如,它应该以一种愚蠢的方式处理子表达式,立即消耗尽可能长的输入)。matcher像往常一样工作,但我们对理想行为的想法现在与其算法不同步。内部表达式的误报(很难注意到)变成了外部表达式的误报(我们注意到并且讨厌)。

第一个失败的正则表达式会匹配什么?我只是部分地跟着你-你能证明为什么正则表达式匹配案例5而不匹配案例6吗?
\p{Alpha}{1100}
意味着整个字符串的剩余部分都被消耗掉了,而不仅仅是被编辑用来解释案例5的
r
(更难,因为我们需要考虑每一种可能性,即“如果它可以,它如何匹配”)@JoannaTurban不,它不会,即使是整个单词的剩余部分。对于整个regexp,最左边的大于最长的规则并不意味着对于任何子表达式,即使包含regexp的make失败,也会自动选择可能最长的匹配。这正是造成混淆的原因:正则表达式本身“回溯”以找到匹配的任何可能性,但负面外观不会“回溯”以找到失败的任何可能性(即,在包含表达式时“成功”)。非常感谢,这正是我所希望的:有人指出我现在显而易见的事情,这个词中的“S”干扰了我在表达式末尾试图匹配的[SX]。所以现在它通过了:
assertTrue(“6个字母匹配,负数”),Pattern.compile(“plusieurs(?!pe\\p{Alpha}{1100}[sx]\\b)\\w+”).matcher(test.find())
这是对我的原始问题的简化,我不确定我的原始问题是否得到了解决(与PHP/PCRE和Java处理前瞻/落后断言之间的差异有关),所以我可能会回来…
pe           literal, matches "pe"
r            matches \p{Alpha}{1,100}
s            matches [sx]
per          literal, would match "per" -- it's always so
             -- let's try to imagine how the rest could match:
sonn         would match \p{Alpha}{1,100}
e            wouldn't match [sx], FAIL
             -- or maybe
s            would match \p{Alpha}{1,100}
o            wouldn't match [sx], FAIL
             -- or maybe yet
so           would match \p{Alpha}{1,100}
n            wouldn't match [sx], FAIL.