Java replaceAll 正则表达式错误

Java replaceAll regex error

我想将除“\*”之外的所有“*”都转换为“.*”

String regex01 = "\*toto".replaceAll("[^\\]\*", ".*");
assertTrue("*toto".matches(regex01));// True

String regex02 = "toto*".replaceAll("[^\\]\*", ".*");
assertTrue("tototo".matches(regex02));// True

String regex03 = "*toto".replaceAll("[^\\]\*", ".*");
assertTrue("tototo".matches(regex03));// Error

如果“*”是第一个字符,则会发生错误: java.util.regex.PatternSyntaxException: 索引 0

附近的悬挂元字符“*”

正确的正则表达式是什么?

您需要在此处使用负后视:

String regex01 = input.replaceFirst("(?<!\\)\*", ".*");

(?<!\\) 是一个负向后视,表示匹配 * 如果它前面没有反斜杠。

示例:

regex01 = "\*toto".replaceAll("(?<!\\)\*", ".*");
//=> \*toto

regex01 = "*toto".replaceAll("(?<!\\)\*", ".*");
//=> .*toto

您必须在正则表达式中考虑以 * 开头的字符串的情况:

(^|[^\\])\*

单个插入符号代表 'beginning of the string' ( 'start anchor' )。

编辑

除了上面的更正之外,replaceAll 调用中的替换字符串必须是 .* 而不是 .* 以免丢失未转义的 * 之前的匹配字符.

这是目前唯一能够处理连续多个转义\的解决方案:

String regex = input.replaceAll("\G((?:[^\\*]|\\[\\*])*)[*]", ".*");

工作原理

让我们打印字符串 regex 以查看正则表达式引擎解析的实际字符串:

\G((?:[^\*]|\[\*])*)[*]

((?:[^\*]|\[\*])*) 匹配非 \* 或转义序列 \\* 的字符序列。我们把所有不想碰的字符都匹配出来,放在一个捕获组里,这样我们就可以放回去了。

上述序列后跟一个未转义的星号,如 [*] 所述。

为了确保当正则表达式无法匹配未转义的*时我们不会"jump",\G用于确保下一次匹配只能从字符串的开头开始,或者从最后一个匹配结束的地方开始。

为什么这么长的解决方案?有必要,因为look-behind构造检查是否连续[= * 之前的 14=] 是奇数或偶数,不受 Java 正则表达式的正式支持。因此,我们需要从左到右消费字符串,同时考虑转义序列,直到遇到未转义的 * 并将其替换为 .*.

测试程序

String inputs[] = {
    "toto*",
    "\*toto",
    "\\*toto",
    "*toto",
    "\\\\*toto",
    "\\*\\\*\*\\\\*"};

for (String input: inputs) {
    String regex = input.replaceAll("\G((?:[^\\*]|\\[\\*])*)[*]", ".*");
    System.out.println(input);
    System.out.println(Pattern.compile(regex));
    System.out.println();
}

示例输出

toto*
toto.*

\*toto
\*toto

\*toto
\.*toto

*toto
.*toto

\\*toto
\\.*toto

\*\\*\*\\*
\.*\\*\*\\.*