为什么Java正则表达式引擎在+;重复?

为什么Java正则表达式引擎在+;重复?,java,regex,fibonacci,nested-reference,Java,Regex,Fibonacci,Nested Reference,我已经编写了一个正则表达式模式来查找斐波那契数(不管为什么,我只是这么做了)。它的工作原理与预期的一样好(): 有人能解释一下这里发生了什么事吗?这是Java正则表达式引擎中的错误吗?错误ID 6984178 有许多与引擎引发的StringIndexOutOfBoundsException相关的错误(特别是这一个错误已被报告并被内部接受为(可能需要一段时间才能在外部数据库中显示) 下面是一个复制bug()的简单模式: 请注意,使用*?或*+只会按预期返回false 问题似乎是由于在前瞻中有对捕获

我已经编写了一个正则表达式模式来查找斐波那契数(不管为什么,我只是这么做了)。它的工作原理与预期的一样好():

有人能解释一下这里发生了什么事吗?这是Java正则表达式引擎中的错误吗?

错误ID 6984178 有许多与引擎引发的
StringIndexOutOfBoundsException
相关的错误(特别是这一个错误已被报告并被内部接受为(可能需要一段时间才能在外部数据库中显示)

下面是一个复制bug()的简单模式:

请注意,使用
*?
*+
只会按预期返回
false

问题似乎是由于在前瞻中有对捕获组的引用时试图回溯贪婪的重复而触发的:越界索引是第一个和第二个
a+
(例如
“aabaaaab”
获取
-3
)之间的长度差

您可能需要调试,以确定错误的确切性质


探索斐波那契模式 在Java引擎上,使用贪婪回溯
+
下面是一个更详细的片段,展示引擎在这种模式下是如何疯狂的:

String FIBONACCI = 
    "(?x) .{0,2} | (?: (?=(\\2|^)) (?=(\\2\\3|^.)) (?=(\\1)) \\2)+ . ";

for (int n = 0; n < 1000; n++) {
    String s = new String(new char[n]);
    try {
        if (s.matches(FIBONACCI)) {
            System.out.printf("%n%s", n);
        }
    } catch (StringIndexOutOfBoundsException e) {
        String index = e.getMessage().replace("String index out of range: ", "");
        System.out.printf(" <%s:%s>", n, index);
    }
}
如您所见,即使使用回溯
+
“循环”,输出也是正确的。事实上,正是因为它是回溯循环,所以特例可以仅限于
{0,1}
,而不是
{0,2}


在Java引擎上,不情愿地回溯
+?
这在Java中可以正常工作。另外,由于不太情愿,我们还可以将特例限制为
{0,1}
():


关于算法 公式 该模式利用以下特性:

这可以通过归纳法证明

模式 让我们使用这个模式的版本(它在Java中工作,锚定后也在C#中工作):

求和
自由空间!“循环”
↓                                                     ↓
(?x.{0,1}|(?:(?=(\2 |^))(?=(\2\3 |^.))(?=(\1))\2+。
\____/   \___________________________________/  ↑    ↑  
基本情况感应情况+Fi+1
对于n=0,1
(断言不计入总和)!
$1:=$2(或用0初始化)
$2:=$2+$3(或用1初始化)
$3:=$1(用于“交换”的临时变量)

Fibonacci序列的生成非常简单,基于
[$1,$2]:=[$2,$2+$1]
转换。由于断言是按顺序执行的,因此引入了一个临时变量(就像单一赋值“伪代码”版本).

我打赌这是因为正则表达式引擎已经厌倦了被要求做一些愚蠢的事情:-)。用正则表达式查找斐波那契数?保利,你疯了!现在这是一个混乱的常规异常…:-)我还在想办法弄清楚你的regexp…看起来像个bug。如果有帮助的话,它似乎通过了4而在5上失败了(应该是匹配的)-。我最近也很无聊,做了一些与a相关的事情,但这并没有那么难:)因为它值得,甚至是.NET works()中的一个可回溯的重复。我认为回溯肯定会导致不匹配。我想即使是我也不完全理解我自己的算法=)。仅供参考,大多数错误报告都涉及格式错误的正则表达式或替换字符串以及它们的处理方式。唯一的错误是它应该抛出PatternSyntaxException(regex)或IllegalArgumentException(replacement),而不是SIOOBE。我试图让他们改变这一点,但没有成功。
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
    String index out of range: -1

    at java.lang.String.charAt(String.java:686)
    at java.lang.Character.codePointAt(Character.java:2335)
    at java.util.regex.Pattern$CharProperty.match(Pattern.java:3344)
    at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:3994)
    at java.util.regex.Pattern$GroupCurly.match0(Pattern.java:3966)
    at java.util.regex.Pattern$GroupCurly.match(Pattern.java:3916)
    at java.util.regex.Pattern$Branch.match(Pattern.java:4114)
    at java.util.regex.Matcher.match(Matcher.java:1127)
    at java.util.regex.Matcher.matches(Matcher.java:502)
    at java.util.regex.Pattern.matches(Pattern.java:930)
    at java.lang.String.matches(String.java:2090)
System.out.println(
   "abaab".matches("(?x) (?: (?=(a+)) \\1 b )* x")
); // StringIndexOutOfBounds: -1
String FIBONACCI = 
    "(?x) .{0,2} | (?: (?=(\\2|^)) (?=(\\2\\3|^.)) (?=(\\1)) \\2)+ . ";

for (int n = 0; n < 1000; n++) {
    String s = new String(new char[n]);
    try {
        if (s.matches(FIBONACCI)) {
            System.out.printf("%n%s", n);
        }
    } catch (StringIndexOutOfBoundsException e) {
        String index = e.getMessage().replace("String index out of range: ", "");
        System.out.printf(" <%s:%s>", n, index);
    }
}
Regex r = new Regex(
  @"(?x) ^.{0,1}$ | ^(?: (?=(\2?)) (?=(\2\3|^.)) (?=(\1)) \2)+ . $ "
);

for (int n = 0; n < 1000; n++) {
  if (r.IsMatch("".PadLeft(n))) {
    Console.Write("{0} ", n);
  }
}
// 0 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 
String FIBONACCI = 
        "(?x) .{0,1} | (?: (?=(\\2|^)) (?=(\\2\\3|^.)) (?=(\\1)) \\2)+? . ";