[英]Java String.replaceAll() with back reference
有一个Java Regex问题:给定一个字符串,如果“*”位于字符串的开头或结尾,请保留它,否则删除它。 例如:
*
- > *
**
- > **
*******
- > **
*abc**def*
- > *abcdef*
答案是:
str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");
我在我的机器上尝试了答案,但它确实有效。 但我不知道它是如何工作的。
根据我的理解,所有匹配的子串应该用$1$2
替换。 但是,它的工作原理如下:
(^\\\\*)
替换为$1
, (\\\\*$)
替换为$2
, \\\\*
换成空。 有人能解释它是如何工作的吗? 更具体地说,如果有|
表达式之间, String.replaceAll()
如何与后向引用一起工作?
先感谢您。
我将尝试解释正则表达式中发生的事情。
str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");
$1
表示第一组是(^\\\\*)
$2
表示第二组(\\\\*$)
当你调用str.replaceAll
,你实际上是在捕获两个组以及其他所有内容,但在替换时,将捕获的文本替换为两个组中捕获的内容。
示例: *abc**def* --> *abcdef*
正则表达式是以*
开头的字符串,它将放入$1
组中,接下来它将继续查找,直到它在组的末尾找到*
并将其存储在#2
。 现在当更换它时将消除所有*
除了存储在$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个捕获的组,一个用于*
开始,另一个用于*
结尾,并在替换中使用反向引用。 这可能在这里起作用,但是对于更大的字符串来说会更慢,这在本演示中采取的步骤中很明显。 使用环视是209对48步。
OP正则表达式的另一个小改进是使用量词 :
(^\*)|(\*$)|\*+
好吧,让我们先来看看你的正则表达式(^\\\\*)|(\\\\*$)|\\\\*
- 它匹配每个*
,如果它在开始时,它被捕获到组1中,如果它是最后,它被捕获到第2组 - 每个其他*
匹配,但不会被放入任何组。
替换模式$ 1 $ 2将每个匹配替换为组1和组2的内容 - 因此,如果在匹配的开头或结尾处有*
,则其中一个组的内容是*
本身,因此被替换通过它自己。 对于所有其他匹配,组仅包含空字符串,因此匹配的*将替换为此空字符串。
您的问题可能是$ 1 $ 2不是字面替换,而是对捕获的组的反向引用。
根据Javadoc:
请注意,替换字符串中的反斜杠()和美元符号($)可能导致结果与将其视为文字替换字符串时的结果不同; 见Matcher.replaceAll。 如果需要,使用Matcher.quoteReplacement(java.lang.String)来抑制这些字符的特殊含义。
你的正则表达式: "(^\\\\*)|(\\\\*$)|\\\\*"
删除引号和String
转义后: (^\\*)|(\\*$)|\\*
有三个部分,通过管道分离|
。 管道意味着OR,这意味着replaceAll()
用第二部分中的东西替换它们: $1$2
。 基本上,第1部分>> $ 1,第二部分>> $ 2,第三部分>> ""
。 注意“第一部分”== $ 1,依此类推......所以它在技术上没有被替换。
1 (^\\*)
是一个捕获组(第一个)。 ^
锚点到字符串开始。 \\*
匹配*
,但需要转义\\
。
2 (\\*$)
再次,一个捕获组(第二个)。 这里的区别在于它以$
结尾
3 \\*
像以前一样,匹配文字*
关于正则表达式需要理解的是,如果它匹配,它将始终采用第一条路径。 虽然字符串开头和结尾的*
s可以与第三部分匹配,但它们匹配第一或第二部分。
其他人给出了非常好的答案,所以我不再重复。 当您正在努力理解诸如此类的问题时,建议暂时将替换字符串添加到替换字符串中,以便清楚每个阶段发生的情况。
例如,使用"<$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为*因为(^\\\\*)
不匹配,但是(\\\\*$)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.