【问题标题】:How to get internal matching result of Java's Matcher.replaceAll?如何获取 Java 的 Matcher.replaceAll 的内部匹配结果?
【发布时间】:2018-11-30 10:49:30
【问题描述】:

我需要替换字符串的某些部分,这些部分可能可用也可能不可用,并且在 Perl s/// 中返回替换的次数。如果有任何东西被替换,这可以很容易地用来回答问题。我正在尝试使用Matcher.replaceAll 在Java 中做同样的事情,虽然这似乎有matching result internally,但我看不到任何访问它的方法。这将使我要么比较返回的字符串和输入的字符串是否存在差异,要么先自己匹配正则表达式,然后根据需要使用replaceAll,这似乎会再次匹配字符串。

或者我遗漏了什么并且有一些可以查询的状态/标志?

【问题讨论】:

  • 当输入是String(而不是另一个CharSequence实现)并且没有匹配时,结果字符串将是与原始字符串相同的对象,因此参考比较就足够了认识到这一点。请注意,这与存在匹配项的情况不同,但替换字符串会产生与原始文本相同的结果。

标签: java regex java-8


【解决方案1】:

如果您使用的是 Java 9,则 Matcher 类提供了一个名为 results() 的方法,该方法返回一个 Stream。你可以这样做:

int count = matcher.results().count();

【讨论】:

  • 感谢 G-d,他们终于让事情变得更容易了 +1。
  • 但是如果你只想找出是否有任何匹配项,你不必计算所有匹配项,所以matcher.results().findAny().isPresent()matcher.results().anyMatch(x -> true) 就足够了,尽管它与只需调用一次matcher.find(),它甚至在 Java 9 之前就可以使用。
  • 我还没有,但您的回答暗示所有旧版本的 Java 都“否”,并最终提供了我正在寻找的东西,所以我接受了。
  • @ThorstenSchöning 要么,您对该解决方案的作用有误解,要么您需要更好地定义您的实际需求。声明 int count = matcher.results().count(); 并不比 int count = 0; while(matcher.find()) count++; 从 Java 1.4 开始有效。
  • 你说得对,我误解了文档,但实现另外调用find,这不是我感兴趣的。我认为它只是返回内部收集的@987654329 状态@ 已经在 replaceAll 期间执行。这就是我感兴趣的,replaceAll 之后匹配器的内部状态。 github.com/AdoptOpenJDK/openjdk-jdk9/blob/master/jdk/src/…
【解决方案2】:

您可以使用模式匹配器并只计算匹配项。下面的代码是一种慢动作,更可控的正则表达式替换方式。例如,我假设您想用cat 替换所有出现的dog

String line = "There was a big dog, larger than the other dog.";
String pattern = "\\bdog\\b";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);

int count = 0;
StringBuffer buff = new StringBuffer();
while (m.find()) {
    m.appendReplacement(buff, "cat");
    ++count;
}

m.appendTail(buff);
System.out.println(buff.toString());
System.out.println("There were " + count + " matches.");

输出:

There was a big cat, larger than the other cat.
There were 2 matches.

请注意,有一个比上面提到的更聪明、更简单的解决方案。我们可以比较正则表达式替换前后的字符串长度。但是,对于我给出的示例,我们不能使用这种方法,因为起始字符串和替换字符串的长度恰好相同。

【讨论】:

  • 为什么是“慢动作”?这正是便捷方法replaceAll 在后台所做的。唯一的区别是它会在StringBuilder 分配之前执行第一个find(),当甚至没有匹配时返回原始字符串,因此循环将是do … while。由于这种优化,您可以使用参考比较来找出是否有任何匹配项,因此即使替换结果与原始字符串的结果字符串内容完全相同,它也会是一个新字符串。但是您的解决方案具有几乎相同的性能。
  • @Holger 我指的是我的代码有点说明String#replaceAll 在幕后所做的事情。无论如何,我认为,接受的答案的单线更好,除非我们都想全部替换 一口气得到计数,在这种情况下,我的答案变得有竞争力。
  • 我是这么理解的,一口气搞定。正如在另一个答案中所说,如果这不是必需的,那么简单的 find() 将告诉您匹配的存在,而无需处理匹配流。您的解决方案就是我要做的,或者,当我事先知道替换字符串中没有组引用时,我会使用匹配的 start()end() 索引来重复 StringBuilder.append(CharSequence,int,int) 这将是更快。
猜你喜欢
  • 2011-08-17
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
相关资源
最近更新 更多