回溯引用在向后查找中的应用

12

你能在后顾之忧中使用回溯引用吗?

假设我想要在一个字符重复两次的位置进行split分割。

    String REGEX1 = "(?<=(.)\\1)"; // DOESN'T WORK!
    String REGEX2 = "(?<=(?=(.)\\1)..)"; // WORKS!

    System.out.println(java.util.Arrays.toString(
        "Bazooka killed the poor aardvark (yummy!)"
        .split(REGEX2)
    )); // prints "[Bazoo, ka kill, ed the poo, r aa, rdvark (yumm, y!)]"

使用REGEX2(其中反向引用位于嵌套在回顾后面的前瞻中)可以正常工作,但是REGEX1在运行时会出现以下错误:

Look-behind group does not have an obvious maximum length near index 8
(?<=(.)\1)
        ^

这有点说得通,我想,一般来说反向引用可以捕获任意长度的字符串(如果正则表达式编译器更聪明一些,它可以确定在这种情况下\1(.),因此具有有限的长度)。
那么有没有办法在回顾后面使用反向引用呢?
如果没有,你是否总是可以使用这个嵌套的前瞻来解决它?还有其他常用技巧吗?

1
有趣的,你的巧妙解决方案加一分。我不使用Java,所以无法尝试它 - 如果反向引用组在环视之外,例如(?<=\\1)(.)会发生什么? - Tim Pietzcker
@Tim:它导致基本相同的“PatternSyntaxException”。顺便说一句,如果有人想玩一下这个问题的变体,我刚刚在CodingBat上创建了一个:http://codingbat.com/prob/p266235 - polygenelubricants
@polygenelubricants 我希望我能给这个正则表达式点赞:(?<=(?=(.)\1)..),至少10次。非常优雅! - Eugene
1个回答

5
看起来你的怀疑是正确的,Java中回溯引用通常不能在后顾中使用。你提出的解决方法将后顾的有限长度明确化,我觉得非常聪明。
我对Python中如何处理这个正则表达式感到好奇。Python只支持固定长度的后顾,不像Java那样是有限长度,但这个正则表达式是固定长度的。我不能直接使用re.split(),因为Python的re.split()从不在空匹配上分割,但我认为我发现了re.sub()中的一个bug:
>>> r=re.compile("(?<=(.)\\1)")
>>> a=re.sub(r,"|", "Bazooka killed the poor aardvark (yummy!)")
>>> a
'Bazo|oka kil|led the po|or a|ardvark (yum|my!)'

回顾先行断言,它匹配位于重复字符之间的文本!


请查看http://stackoverflow.com/questions/2628534/codingbat-plusout-using-regex以获取更多有趣的正则表达式。 - polygenelubricants
re.split() 不会在空匹配上分割,这太愚蠢了。为什么他们要这样做呢?我认为有很多时候你只是想基于断言而不是实际的非空分隔符进行分割。 - polygenelubricants
我已经在Python错误跟踪器上询问了同样的问题。这可能是无意的,但被保留下来以避免兼容性问题;目前正在进行重大的正则表达式引擎改进,但可能需要一段时间才能将新的正则表达式模块合并到标准库中。 - Tim Pietzcker
Java的正则表达式包最初存在相同的错误,即回顾先行条件未锚定在当前匹配位置,但在JDK 1.6中已经修复。 - Alan Moore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接