Java正则表达式总是失败

12

我有一个Java正则表达式模式和一个句子,我想完全匹配它,但对于一些句子它错误地失败了。为什么会这样?(为简单起见,我不使用我的复杂正则表达式,而只使用“.*”)

System.out.println(Pattern.matches(".*", "asdf"));
System.out.println(Pattern.matches(".*", "[11:04:34] <@Aimbotter> 1 more thing"));
System.out.println(Pattern.matches(".*", "[11:04:35] <@Aimbotter> Dialogue: 0,0:00:00.00,0:00:00.00,Default,{Orginal LV,0000,0000,0000,,[???]??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????} "));
System.out.println(Pattern.matches(".*", "[11:04:35] <@Aimbotter> Dialogue: 0,0:00:00.00,0:00:00.00,Default,{Orginal LV,0000,0000,0000,,[???]????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????} "));

输出:

true
true
true
false

请注意,第四句话在问号之间包含了10个Unicode控制字符\u0085,这些字符通常无法由普通字体显示出来。实际上,第三句和第四句话包含相同数量的字符!


这尤其奇怪,因为Java是一个Unicode正则表达式引擎... - josh.trow
如果Java不知道Unicode行终止符(http://www.fileformat.info/info/unicode/char/85/index.htm),那将会更糟糕。 - rurouni
@tchrist 很快就会出现并告诉我们所有关于 Java 正则表达式引擎有多糟糕的问题。 - aioobe
4个回答

13

使用

Pattern.compile(".*",Pattern.DOTALL)

如果你想匹配控制字符,可以使用"."。默认情况下,它只匹配可打印的字符。

从JavaDoc中可以看到:

"在dotall模式下,表达式"."匹配任何字符,包括行终止符。默认情况下,该表达式不匹配行终止符。

也可以通过嵌入式标志表达式(?s)来启用Dotall模式。(s代表"single-line"模式,这是Perl中的命名。)"

Pattern中的代码(其中包含\u0085):

/**
 * Implements the Unicode category ALL and the dot metacharacter when
 * in dotall mode.
 */
static final class All extends CharProperty {
boolean isSatisfiedBy(int ch) {
    return true;
}
}

/**
 * Node class for the dot metacharacter when dotall is not enabled.
 */
static final class Dot extends CharProperty {
boolean isSatisfiedBy(int ch) {
    return (ch != '\n' && ch != '\r'
                && (ch|1) != '\u2029'
                && ch != '\u0085');
    }
}

谢谢,(?s)有效。我没有尝试Pattern.DOTALL,因为我有大量不同的编译模式,并且我只需要在一个模式中使用(?s)一次(在我包含在大多数模式中的字符串常量中)。 - Zom-B

4
答案就在问题里:10个Unicode控制字符\u0085。
Unicode控制字符和\n一样,都无法被.*识别。

2

Unicode /u0085代表换行符,因此您需要在正则表达式的开头添加(?s)(表示点会匹配所有字符),或者在编译正则表达式时添加该标志。

Pattern.matches("(?s).*", "blahDeBlah\u0085Blah")

1
不是(?m)- 多行模式意味着^$匹配行的开头/结尾。你需要使用单行模式的(?s)。是的,这很令人困惑(想法是“将整个输入视为单行”)。 - Tim Pietzcker

1
我认为问题在于\u0085代表换行符。如果你想要多行匹配,你需要使用Pattern.MULTILINEPattern.DOTALL。这与Unicode无关——'\n'也会失败。
使用方法:Pattern.compile(regex, Pattern.DOTALL).matcher(input).matches()

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