有一个Java Regex问题: 给定一个字符串,如果" *"在字符串的开头或结尾处,保留它,否则,将其删除。 例如:
*
- > *
**
- > **
*******
- > **
*abc**def*
- > *abcdef*
答案是:
str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");
我在我的机器上尝试了答案,但它确实有效。但我不知道它是如何运作的。
根据我的理解,所有匹配的子字符串应替换为$1$2
。但是,它的工作原理如下:
(^\\*)
已替换为$1
,(\\*$)
已替换为$2
,\\*
替换为空。有人可以解释它是如何工作的吗?更具体地说,如果表达式之间存在|
,String.replaceAll()
如何使用后向引用?
提前谢谢。
答案 0 :(得分:21)
我会尝试解释正则表达式中发生的事情。
str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");
$1
代表第一组(^\\*)
$2
代表第二组(\\*$)
当您致电str.replaceAll
时,您实际上是在捕获两个群组以及其他所有内容,但在更换时,将捕获的文本替换为两个群组中捕获的内容。
示例:*abc**def* --> *abcdef*
发现正则表达式以*
开头的字符串,它将放入$1
组,接下来它会继续查找,直到它在组末尾找到*
并将其存储在{{1} }}。现在,当替换它时将消除除#2
或*
$1
之外的所有$2
有关详细信息,请参阅Capture Groups
答案 1 :(得分:2)
您可以在正则表达式中使用外观:
String repl = str.replaceAll("(?<!^)\\*+(?!$)", "");
RegEx分手:
(?<!^) # If previous position is not line start
\\*+ # match 1 or more *
(?!$) # If next position is not line end
OP的正则表达式是:
(^\*)|(\*$)|\*
它使用2个捕获的组,一个用于*
,另一个用于*
,并且在替换时使用后向引用。这可能在这里起作用,但是对于更大的字符串来说,如# of steps taken in this demo中所显示的那样更慢。这是使用环视的 209 vs 48 步骤。
OP正则表达式的另一个较小的改进是使用量词:
(^\*)|(\*$)|\*+
答案 2 :(得分:1)
好吧,让我们首先看看你的正则表达式(^\\*)|(\\*$)|\\*
- 它匹配每个*
,如果它在开始时,它被捕获到组1中,如果它在结尾,它被捕获到第2组 - 每个其他*
匹配,但不会被放入任何组。
替换模式$ 1 $ 2将每个匹配替换为组1和组2的内容 - 因此,如果在匹配的开头或结尾有*
,则其中一个组的内容为那个*
本身因而被它自己取代。对于所有其他匹配,组仅包含空字符串,因此匹配的*将替换为此空字符串。
您的问题可能是$ 1 $ 2不是字面替换,而是对捕获组的反向引用。
答案 3 :(得分:0)
根据Javadoc:
请注意,替换字符串中的反斜杠()和美元符号($)可能会导致结果与将其视为文字替换字符串时的结果不同;见Matcher.replaceAll。如果需要,使用Matcher.quoteReplacement(java.lang.String)来抑制这些字符的特殊含义。
你的正则表达式:"(^\\*)|(\\*$)|\\*"
删除引号和String
转义后:(^\*)|(\*$)|\*
有三个部分,由管道|
分隔。管道意味着OR,这意味着replaceAll()
将其替换为第二部分中的内容:$1$2
。基本上,第一部分&gt;&gt; $ 1,第二个&gt;&gt; $ 2,第三个&gt;&gt; ""
。注意&#34;第一部分&#34; == 1美元,等等......所以它在技术上没有被替换。
1 (^\*)
是一个捕获组(第一个)。 ^
锚定到字符串start。 \*
匹配*
,但需要转义\
。
2 (\*$)
再次,一个捕获组(第二个)。这里的区别在于它以$
3 \*
匹配文字*
你需要了解的关于正则表达式的事情是,如果它匹配,它将始终采用第一条路径。虽然字符串开头和结尾的*
可以与第三部分匹配,但它们会匹配第一部分或第二部分。
答案 4 :(得分:0)
其他人给出了非常好的答案,所以我不会重复这些答案。当您正在努力理解诸如此类的问题时,建议是临时向替换字符串添加分隔符,以便清楚每个阶段发生的情况。
e.g。使用"<$1|$2>"
这将得到<x|y>
的结果,其中x为$ 1,y为$ 2
String str = "*ab**c*d*";
str.replaceAll("(^\\*)|(\\*$)|\\*", "<$1|$2>");
结果是:<*|>ab<|><|>c<|>d<|*>
因此对于第一个星号,$ 1 = *和$ 2为空,因为(^\\*)
匹配。
对于中间字符串星号,$ 1和$ 2都是空的,因为两个捕获组都不匹配。
对于最终的星号,$ 1为空,$ 2为*,因为(^\\*)
不匹配,但(\\*$)
不匹配。