【问题标题】:Regex look-behind without obvious maximum length in JavaJava中没有明显最大长度的正则表达式后视
【发布时间】:2010-12-04 22:44:34
【问题描述】:

我一直认为 Java 的 regex-API(以及许多其他语言)中的 look-behind 断言必须具有明显的长度。因此,look-behinds 中不允许使用 STAR 和 PLUS 量词。

优秀的在线资源regular-expressions.info 似乎证实了我的(部分)假设:

"[...] Java 更进一步: 允许有限重复。你还在 不能使用星号或加号,但您 可以使用问号和 带有 max 参数的花括号 指定的。 Java 承认事实 有限重复可以是 重写为字符串的交替 具有不同但固定的长度。 不幸的是,JDK 1.4 和 1.5 使用时有一些错误 内部交替。这些 已在 JDK 1.6 中修复。 [...]”

-- http://www.regular-expressions.info/lookaround.html

只要后视中字符范围的总长度小于或等于 Integer.MAX_VALUE,就可以使用大括号。所以这些正则表达式是有效的:

"(?<=a{0,"   +(Integer.MAX_VALUE)   + "})B"
"(?<=Ca{0,"  +(Integer.MAX_VALUE-1) + "})B"
"(?<=CCa{0," +(Integer.MAX_VALUE-2) + "})B"

但这些不是:

"(?<=Ca{0,"  +(Integer.MAX_VALUE)   +"})B"
"(?<=CCa{0," +(Integer.MAX_VALUE-1) +"})B"

但是,我不明白以下内容:

当我在 look-behind 中使用 * 和 + 量词运行测试时,一切顺利(请参阅输出 Test 1 和 Test 2强>)。

但是,当我在 Test 1 和 Test 2 的 look-behind 开头添加一个字符时,它会中断 (见输出Test 3)。

使 Test 3 中的贪婪 * 不情愿没有效果,它仍然会中断(参见 Test 4)。

这是测试工具:

public class Main {

    private static String testFind(String regex, String input) {
        try {
            boolean returned = java.util.regex.Pattern.compile(regex).matcher(input).find();
            return "testFind       : Valid   -> regex = "+regex+", input = "+input+", returned = "+returned;
        } catch(Exception e) {
            return "testFind       : Invalid -> "+regex+", "+e.getMessage();
        }
    }

    private static String testReplaceAll(String regex, String input) {
        try {
            String returned = input.replaceAll(regex, "FOO");
            return "testReplaceAll : Valid   -> regex = "+regex+", input = "+input+", returned = "+returned;
        } catch(Exception e) {
            return "testReplaceAll : Invalid -> "+regex+", "+e.getMessage();
        }
    }

    private static String testSplit(String regex, String input) {
        try {
            String[] returned = input.split(regex);
            return "testSplit      : Valid   -> regex = "+regex+", input = "+input+", returned = "+java.util.Arrays.toString(returned);
        } catch(Exception e) {
            return "testSplit      : Invalid -> "+regex+", "+e.getMessage();
        }
    }

    public static void main(String[] args) {
        String[] regexes = {"(?<=a*)B", "(?<=a+)B", "(?<=Ca*)B", "(?<=Ca*?)B"};
        String input = "CaaaaaaaaaaaaaaaBaaaa";
        int test = 0;
        for(String regex : regexes) {
            test++;
            System.out.println("********************** Test "+test+" **********************");
            System.out.println("    "+testFind(regex, input));
            System.out.println("    "+testReplaceAll(regex, input));
            System.out.println("    "+testSplit(regex, input));
            System.out.println();
        }
    }
}

输出:

********************** Test 1 **********************
    testFind       : Valid   -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true
    testReplaceAll : Valid   -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa
    testSplit      : Valid   -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa]

********************** Test 2 **********************
    testFind       : Valid   -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true
    testReplaceAll : Valid   -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa
    testSplit      : Valid   -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa]

********************** Test 3 **********************
    testFind       : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
      ^
    testReplaceAll : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
      ^
    testSplit      : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6
(?<=Ca*)B
      ^

********************** Test 4 **********************
    testFind       : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
       ^
    testReplaceAll : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
       ^
    testSplit      : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7
(?<=Ca*?)B
       ^

我的问题可能很明显,但我还是会问:谁能向我解释为什么 Test 1 和 2 失败,以及 测试 3 和 4 没有? 我原以为它们都会失败,而不是一半工作,一半失败。

谢谢。

PS。我正在使用:Java 版本 1.6.0_14

【问题讨论】:

    标签: java regex


    【解决方案1】:

    查看 Pattern.java 的源代码可以发现,'*' 和 '+' 是作为 Curly 的实例实现的(它是为 curly 运算符创建的对象)。所以,

    a*
    

    实现为

    a{0,0x7FFFFFFF}
    

    和

    a+
    

    实现为

    a{1,0x7FFFFFFF}
    

    这就是为什么你看到卷曲和星号的行为完全相同的原因。

    【讨论】:

    • 我没有想到要查看 Pattern 的来源......我傻了。谢谢!现在说得通了。
    • 我不能没有 Eclipse 的众多原因之一是我的 ctrl 单击手指发痒(如果您不使用 Eclipse,这意味着“打开定义此名称的源文件")。
    • 谢谢,我从来没有费心将源附加到 Eclipse。我现在肯定会这样做。谢谢。
    【解决方案2】:

    这是一个错误:http://bugs.sun.com/view_bug.do?bug_id=6695369

    Pattern.compile() 在无法确定后向匹配的最大可能长度时总是应该抛出异常。

    【讨论】:

    • 嘿艾伦,我想你很快就会来的!谢谢,我知道这曾经是一个错误(在 1.5 中),但我记得引擎总是评估为 false 或其他东西。现在它确实正确地评估了结果,所以我认为这个错误在 1.6 中得到了修复。我一定是记错了。谢谢(你的)信息。巴特(又名 prometheuzz)
    • 该错误已修复;这是 1.6 中引入的新功能。 ://
    • 啊,太好了:|。感谢您的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多