【问题标题】:Java Matcher class to implement toFindResult()Java Matcher 类实现 toFindResult()
【发布时间】:2021-04-07 09:07:41
【问题描述】:

根据this questionfindmatches() 有很大的不同,仍然都以某种形式提供结果。

作为一种实用程序,toMatchResult 函数返回 matches() 操作的当前结果。我希望我在(1) 下的假设是有效的。 (正则表达式为here

        String line = "aabaaabaaabaaaaaab";
        String regex = "(a*b)a{3}";
        Matcher matcher = Pattern.compile(regex).matcher(line);
        matcher.find();
//        matcher.matches();(1) --> returns false because the regex doesn't match the whole string
        String expectingAab = matcher.group(1);
        System.out.println("actually: " + expectingAab);

不幸的是,以下方法根本不起作用(例外:未找到匹配项):

        String line = "aabaaabaaabaaaaaab";
        String regex = "(a*b)a{3}";
        String expectingAab = Pattern.compile(regex).matcher(line).toMatchResult().group(1);
        System.out.println("actually: " + expectingAab);

这是为什么呢?我的第一个假设是它不起作用,因为正则表达式应该匹配整个字符串;但是字符串值aabaaa 也会抛出相同的异常......

当然,匹配器需要使用find() 设置为正确的状态,但是如果我想使用oneliner 怎么办?我实际上为此实现了一个实用程序类:


protected static class FindResult{
    private final Matcher innerMatcher;
    public FindResult(Matcher matcher){
        innerMatcher = matcher;
        innerMatcher.find();
    }
    public Matcher toFindResult(){
        return  innerMatcher;
    }
}

public static void main(String[] args){
    String line = "aabaaabaaabaaaaaab";
    String regex = "(a*b)a{3}";
    String expectingAab = new FindResult(Pattern.compile(regex).matcher(line)).toFindResult().group(1);
    System.out.println("actually: " + expectingAab);
}

我很清楚这不是创建 oneliner 的最佳解决方案,尤其是因为它给垃圾收集器带来了沉重的负担..

有没有更简单、更好的解决方案?

值得注意的是,我正在寻找解决方案 java8.匹配逻辑在 java 9 之上的工作方式不同。

【问题讨论】:

  • 如果你不想创建新对象,为什么不直接使用静态方法呢?您不需要存储任何状态。你只是不喜欢MatcherUtils.findResult(Pattern.compile("...").matcher("...")) 这样的美学吗?
  • 这实际上是一个有效的观点!谢谢,如果没有内置功能,我也会接受这个答案。

标签: java regex java-8 one-liner


【解决方案1】:

toMatchResult()方法返回上一次匹配操作的状态,无论是find()lookingAt()还是matches()

你的线路

String expectingAab = Pattern.compile(regex).matcher(line).toMatchResult().group(1);

不调用任何这些方法,因此,永远不会有以前的匹配,并且总是产生IllegalStateException: No match found

如果你想要一个单行来提取第一个匹配的第一组,你可以简单地使用

String expectingAab = line.replaceFirst(".*?(a*b)a{3}.*", "$1");

模式在实际匹配模式之前需要.*?,在实际匹配模式之后需要.*,以消耗剩余的字符串并仅将第一组作为其内容。需要注意的是,如果不存在匹配项,它将评估为原始字符串。

所以如果你想要matches 而不是find 语义,你可以使用

String expectingNoMatch = line.replaceFirst("^(a*b)a{3}$", "$1");

它将评估为带有示例输入的原始字符串,因为它不匹配。

如果您不希望实用程序方法创建 FindResult 实例,只需使用直接的 static 方法即可。

但是,这是过早优化的典型案例。 Pattern.compile 调用创建了一个 Pattern 对象,加上一堆表示模式元素的内部节点对象,matcher 调用创建了一个 Matcher 实例加上数组来保存组,toMatchResult 调用创建另一个对象实例,当然,group(1) 调用不可避免地会创建一个表示结果的新字符串实例。

FindResult 实例的创建是这一行中最便宜的。如果您关心性能,如果您多次使用该模式,则保留Pattern.compile 的结果,因为这是最昂贵的操作,并且Pattern 实例是不可变和可共享的,正如其文档中明确指出的那样。

当然,字符串方法replaceFirstreplaceAll 没有任何作用,但在后台执行相同的步骤。

【讨论】:

  • 过早的优化是一个非常好的点。 You can't avoid creating objects :)
  • @Sweeper 确实,对象创建的实际成本取决于其构造函数的工作,而不是分配,即字符串创建复制整个字符内容,Pattern.compile 做了很多实际工作来处理图案。而没有实际构建工作的临时对象的成本,即对 GC 的影响,已经在我的this answer 中进行了分析......
【解决方案2】:

该方法不需要实例字段即可工作。它可以只是一个静态助手:

class MatcherUtils {
  public static MatchResult findResult(Matcher matcher) {
    matcher.find();
    return matcher.toMatchResult();
  }
}

用法:

MatchResult result = MatcherUtils.findResult(Pattern.compile("...").matcher("..."));

请注意,当find 找不到任何东西时,您可能想要处理这种情况(感谢单行,Holger!):

class MatcherUtils {
  public static Optional<MatchResult> findResult(Matcher matcher) {
    return Optional.of(matcher)
             .filter(Matcher::find)
             .map(Matcher::toMatchResult);
    /*
    if (matcher.find()) {
      return Optional.of(matcher.toMatchResult());
    } else {
      return Optional.empty();
    }
    */
  }
}

【讨论】:

    猜你喜欢
    • 2019-01-18
    • 2011-12-08
    • 1970-01-01
    • 2012-03-09
    • 2011-10-14
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    相关资源
    最近更新 更多