正则表达式元字符“*”和“*?”在 JAVA 的 replaceAll() 方法中行为异常[重复]

Posted

技术标签:

【中文标题】正则表达式元字符“*”和“*?”在 JAVA 的 replaceAll() 方法中行为异常[重复]【英文标题】:Regexp metachars "*" and "*?" in the JAVA's replaceAll() method behave oddly [duplicate] 【发布时间】:2013-01-19 10:50:29 【问题描述】:

可能重复:String.replaceAll() anomaly with greedy quantifiers in regexStrange behavior in regexes

虽然

"a".replaceAll("a", "b")
"a".replaceAll("a+", "b")
"a".replaceAll("a+?", "b")

全部返回b,为什么呢

"a".replaceAll("a*", "b")

返回bb

"a".replaceAll("a*?", "b")

返回bab?

【问题讨论】:

仍在寻找副本... 给你:***.com/questions/9228509/strange-behavior-in-regexes 我认为 Perl 在第二种情况下返回 bbb 。这更有意义。一种*?不匹配“a”很奇怪。 @jdb 我认为我发布的链接很好地解释了这一点。 回显一个 | perl -i -pe "s/a*?/b/g" 【参考方案1】:

"a".replaceAll("a*", "b")

* - 0 或更多,所以在 `a*` 中
1. a - replaced by b
2. * - is also true as empty string is true for *,so, replaced by b 

"a".replaceAll("a*?", "b")

1. *? - makes anything non-greedy means it makes regrex to match as little
        as possible,
2. so,the pre and post empty string would be replaced by "b" and 
   as a*? is non-greedy, it will not replace it 

【讨论】:

【参考方案2】:

这是因为零宽度匹配。

"a".replaceAll("a*", "b")

将匹配两次:

    在字符串的开头尝试匹配,贪婪的* 消耗a 作为匹配。

    前进到字符串中的下一个位置(现在在字符串的末尾),尝试在那里匹配,空字符串匹配。

    " a "
     \| \___ 2. match empty string
      \_____ 1. match "a"
    

"a".replaceAll("a*?", "b")

也会匹配两次:

    尝试在字符串开头匹配,非贪心*?匹配空字符串而不消耗a

    前进到字符串中的下一个位置(现在在字符串的末尾),尝试在那里匹配,空字符串匹配。

    " a "
     \  \___ 2. match empty string
      \_____ 1. match empty string
    

【讨论】:

我喜欢这些图表;-)【参考方案3】:
"a".replaceAll("a*", "b")

首先将a 替换为b,然后将指针移过b。然后它匹配字符串的结尾,并替换为b。因为它匹配一个空字符串,所以它推进指针,从字符串中掉出来,然后结束,产生bb

"a".replaceAll("a*?", "b")

首先匹配字符串的开头并替换为b。它与a 不匹配,因为a*? 中的? 表示“非贪婪”(尽可能少匹配)。由于它匹配一个空字符串,它推进指针,跳过a。然后它匹配字符串的结尾,替换为b并从字符串中掉出来,导致bab。最终结果与您执行"a".replaceAll("", "b") 时相同。

【讨论】:

@mikeslattery 如果指针没有前进,那么只要你匹配一个空字符串,替换器就会陷入无限循环。 打印 bbb。对我来说似乎更合乎逻辑:echo a | perl -i -pe "s/a*?/b/g" @jdb perl 中的规则是什么?如果匹配为空,则指针不会移动,但下一个匹配被限制为非空,通常的规则是先回溯最后一组? 前进到下一个字符,因为空格是匹配的,但不能说明问题。我不知道 Perl 规则是什么,但它更有意义。 Python 也有同样的错误。 @jdb 我不会称之为错误。这确实有道理,而且绝对是故意的。 javascript 的行为与 Java 相同(跳过一个字符)。

以上是关于正则表达式元字符“*”和“*?”在 JAVA 的 replaceAll() 方法中行为异常[重复]的主要内容,如果未能解决你的问题,请参考以下文章

java正则表达式,要求字符串只能包含数字、英文大小写、以及“-”符号

java正则表达式中是啥意思

java正则表达式

Java笔记:正则表达式

正则表达式元字符“*”和“*?”在 JAVA 的 replaceAll() 方法中行为异常[重复]

java中,正则表达式,如何过滤除数字和字母之外的其它字符??