使用`\ R`进行Java-8正则表达式负向观察

在回答另一个问题时 ,我写了一个正则表达式来匹配所有空格,最多包括一个换行符。 我使用负面的lookbehind为\R linebreak matcher做了这个:

 ((?<!\R)\s)* 

后来我在考虑它,我说,哦,不,如果有\r\n ? 当然它会抓住第一个破线字符\r \n然后我会被我的下一个字符串前面的虚假\n卡住,对吗?

所以我回去测试(并且可能修复)它。 但是,当我测试模式时,它匹配整个\r\n 。 它与人们可能期望的\r \n离开\n不匹配。

 "\r\n".matches("((?<!\\R)\\s)*"); // true, expected false 

但是,当我使用\R 文档中提到的“等效”模式时,它返回false。 这是Java的一个错误,还是有匹配的正当理由?

构造\R是一个 ,它将子表达式包围成一个primefaces组(?> parts )

这就是为什么它不会将它们分开。

注意:如果Java在lookbehind中接受固定的替换,使用\R是正常的,但如果引擎没有,则会抛出exception。

实现#1。 文档错了

资料来源: https : //docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

这里说:

Linebreak matcher

……相当于\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

但是,当我们尝试使用“等效”模式时,它返回false:

 String _R_ = "\\R"; System.out.println("\r\n".matches("((?"+_R_+")"; System.out.println("\r\n".matches("((? 

所以Javadoc应该说:

......相当于(?

每个Sherman在Oracle JDK-8176029上更新于2017年3月9日:

“api doc没错,实现错误(当”0x0d + 0x0a + next.match()“失败时,无法回溯”0x0d + next.match()“


实现#2。 Lookbehinds不仅向后看

尽管有这个名字,但是后视不仅能够向后看,而且可以包括甚至跳过当前位置。

请考虑以下示例(来自rexegg.com ):

 "_12_".replaceAll("(?<=_(?=\\d{2}_))\\d+", "##"); // _##_ 

“这很有趣有几个原因。首先,我们在一个后视镜中有一个前瞻,即使我们应该向后看,这个前瞻者通过匹配两个数字和尾随下划线跳过当前位置。这是杂技。”

这对于我们的\R的例子意味着什么,即使我们当前的位置可能是\n ,这也不会阻止后视识别它的\r后跟\n ,然后将两者绑定在一起作为一个primefaces组,并且因此拒绝将当前位置背后的\r部分识别为单独匹配。

注意:为简单起见,我使用了诸如“我们当前位置是\n ”之类的术语,但这并不是内部发生的精确表示。