繁体   English   中英

带有后引用的Java String.replaceAll()

[英]Java String.replaceAll() with back reference

有一个Java Regex问题:给定一个字符串,如果“*”位于字符串的开头或结尾,请保留它,否则删除它。 例如:

  1. * - > *
  2. ** - > **
  3. ******* - > **
  4. *abc**def* - > *abcdef*

答案是:

str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");

我在我的机器上尝试了答案,但它确实有效。 但我不知道它是如何工作的。

根据我的理解,所有匹配的子串应该用$1$2替换。 但是,它的工作原理如下:

  1. (^\\\\*)替换为$1
  2. (\\\\*$)替换为$2
  3. \\\\*换成空。

有人能解释它是如何工作的吗? 更具体地说,如果有| 表达式之间, String.replaceAll()如何与后向引用一起工作?

先感谢您。

我将尝试解释正则表达式中发生的事情。

str.replaceAll("(^\\*)|(\\*$)|\\*", "$1$2");

$1表示第一组是(^\\\\*) $2表示第二组(\\\\*$)

当你调用str.replaceAll ,你实际上是在捕获两个组以及其他所有内容,但在替换时,将捕获的文本替换为两个组中捕获的内容。

示例: *abc**def* --> *abcdef*

正则表达式是以*开头的字符串,它将放入$1组中,接下来它将继续查找,直到它在组的末尾找到*并将其存储在#2 现在当更换它时将消除所有*除了存储在$1$2所有*

有关更多信息,请参阅捕获组

您可以在正则表达式中使用外观:

String repl = str.replaceAll("(?<!^)\\*+(?!$)", "");

RegEx演示

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM