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
\*\\*\*\\*
\.*\\*\*\\.*
我想将除“\*”之外的所有“*”都转换为“.*”
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
\*\\*\*\\*
\.*\\*\*\\.*