java比较两个Pattern对象

时间:2012-04-07 13:49:36

标签: java regex

是否有一种比较两个Pattern对象的简单方法?

我有Pattern使用正则表达式"//"进行编译,以检查代码中的注释。

由于有几个正则表达式来描述注释,我想找到一种方法来区分它们。

怎么做? Pattern类没有实现equals方法。

9 个答案:

答案 0 :(得分:6)

也许我不完全理解这个问题。但正如您在以下示例中所看到的,每个Java对象都有一个默认的java.lang.Object.equals(Object)方法。此方法比较对象的引用,即使用==运算符。


package test;

import java.util.regex.Pattern;

public class Main {

  private static final Pattern P1 = Pattern.compile("//.*");
  private static final Pattern P2 = Pattern.compile("//.*");

  public static void main(String[] args) {
    System.out.println(P1.equals(P1));
    System.out.println(P1.equals(P2));
    System.out.println(P1.pattern().equals(P1.pattern()));
    System.out.println(P1.pattern().equals(P2.pattern()));
  }
}

输出:


true
false
true
true

答案 1 :(得分:5)

您可以通过比较调用Patternpattern()的结果来比较toString个对象,但这不符合您的要求(如果我正确理解您的问题)。具体来说,这会比较传递给Pattern.compile(...)工厂方法的字符串。

没有简单的方法来测试两个不同的正则表达式是否相同。例如,".+""..*"代表等效的正则表达式,但没有直接的方法可以使用Pattern API来确定这一点。

(在一般情况下,我甚至不知道问题是否理论上可解决......)


我也想对已接受的答案发表评论。作者提供了一些他声称​​显示的代码,模式的equals方法继承自Object。实际上,他看到的输出一致与...但它不会显示

知道是否是这种情况的正确方法是查看javadoc ...其中equals方法列在继承方法列表中。这是确定的。

那么为什么示例不能显示作者所说的内容呢?

  1. 两种方法的行为方式可能相同,但实现方式不同。如果我们将Pattern类视为黑盒子,那么我们就无法证明这种情况并未发生。 (或者至少......不是没有使用反射。)

  2. 作者只在一个平台上运行此功能。其他平台可能表现不同。

  3. 关于第二点,我的回忆是,在Pattern的早期实现中(在Java 1.4中),Pattern.compile(...)方法保留了最近编译的模式对象的缓存 1 。如果您编译了两次特定模式字符串,则第二次可能会获得与第一次返回的相同的对象。这将导致测试代码输出:

      true
      true
      true
      true
    

    但这表明了什么?是否表示Pattern会覆盖Object.equals?不!

    这里的教训是,您应该通过查看javadoc来了解Java库方法主要的行为:

    • 如果你写了一个“黑匣子”测试,你可能会得出不正确的结论......或者至少得出的结论可能不适用于所有平台。

    • 如果您的结论基于“阅读代码”,则可能会得出对其他平台无效的结论。


    1 - 即使我的回忆不正确,这样的实现也会与Pattern.compile(...)方法的javadoc一致。他们没有说每个compile调用都会返回一个新的Pattern对象。

答案 2 :(得分:3)

出于神秘的原因,Pattern对象没有实现equals()。例如,这个简单的单元测试将失败:

    @Test
    public void testPatternEquals() {
        Pattern p1 = Pattern.compile("test");
        Pattern p2 = Pattern.compile("test");
        assertEquals(p1, p2); // fails!
    }

最常见的解决方法似乎是比较Pattern对象的字符串表示(返回用于创建Pattern的String):

    @Test
    public void testPatternEquals() {
        Pattern p1 = Pattern.compile("test");
        Pattern p2 = Pattern.compile("test");
        assertEquals(p1.toString(), p2.toString()); // succeeds!
    }

答案 3 :(得分:2)

Pattern不是String。为什么不只比较编译Pattern s的正则表达式?

答案 4 :(得分:1)

我知道自动机可以解决你的问题。但那可能很复杂。 粗略地说,你应该至少比较pattern.pattern()pattern.flags(),尽管这还不足以决定两个正则表达式是否相同。

答案 5 :(得分:0)

您可以比较已制作模式的字符串表示形式:

Pattern p1 = getPattern1();
Pattern p2 = getPattern2();
if (p1.pattern().equals(p2.pattern())){
    // your code here
}

答案 6 :(得分:0)

我想我已经明白了这个问题,因为我搜索了比较Pattern的方法,我最终在这里(可能两年太晚了,很抱歉......)。

我正在编写测试,我需要知道我的方法是否返回预期的模式。虽然通过toString()pattern()的文本可能相同,但标志可能不同,使用模式时的结果可能会出乎意料。

前段时间我写了toString()的一般实现。它收集包括private个字段在内的所有字段,并构造一个可用于记录并显然可用于测试的字符串。它表明,在编译两个相同的模式时,字段rootmatchRoot是不同的。假设这两者并不与平等相关,并且因为有一个字段flag,如果不完美,我的解决方案就相当不错。

/**
 * Don't call this method from a <code>toString()</code> method with
 * <code>useExistingToString</code> set to <code>true</code>!!!
 */
public static String toString(Object object, boolean useExistingToString, String... ignoreFieldNames) {
  if (object == null) {
    return null;
  }

  Class<? extends Object> clazz = object.getClass();
  if (useExistingToString) {
    try {
      // avoid the default implementation Object.toString()
      Method methodToString = clazz.getMethod("toString");
      if (!methodToString.getDeclaringClass().isAssignableFrom(Object.class)) {
        return object.toString();
      }
    } catch (Exception e) {
    }
  }

  List<String> ignoreFieldNameList = Arrays.asList(ignoreFieldNames);
  Map<String, Object> fields = new HashMap<String, Object>();
  while (clazz != null) {
    for (Field field : clazz.getDeclaredFields()) {
      String fieldName = field.getName();
      if (ignoreFieldNameList.contains(fieldName) || fields.containsKey(fieldName)) {
        continue;
      }

      boolean accessible = field.isAccessible();
      if (!accessible) {
        field.setAccessible(true);
      }
      try {
        Object fieldValue = field.get(object);
        if (fieldValue instanceof String) {
          fieldValue = stringifyValue(fieldValue);
        }
        fields.put(fieldName, fieldValue);
      } catch (Exception e) {
        fields.put(fieldName, "-inaccessible- " + e.getMessage());
      }
      if (!accessible) {
        field.setAccessible(false);
      }
    }
    // travel upwards in the class hierarchy
    clazz = clazz.getSuperclass();
  }

  return object.getClass().getName() + ": " + fields;
}

public static String stringifyValue(Object value) {
  if (value == null) {
    return "null";
  }
  return "'" + value.toString() + "'";
}

测试是绿色的:

String toString1 = Utility.toString(Pattern.compile("test", Pattern.CASE_INSENSITIVE), false, "root", "matchRoot");
String toString2 = Utility.toString(Pattern.compile("test", Pattern.CASE_INSENSITIVE), false, "root", "matchRoot");
assertEquals(toString1, toString2);

答案 7 :(得分:0)

要确定两个Pattern对象是否相同,最简单的方法是比较实际字符串模式用于创建该模式的标志:

boolean isPatternEqualToPattern(final Pattern p1, final Pattern p2) {
    return p1.flags() == p2.flags() &&
        p1.pattern().equals(p2.pattern());
}

答案 8 :(得分:0)

虽然其他答案可能会解决问题,但我认为它们不是解决问题的真正答案。

如果你真的想比较两种模式,你基本上想要比较两种常规语言

为此,cs stackexchange已经发布了一个解决方案: https://cs.stackexchange.com/questions/12876/equivalence-of-regular-expressions

检查常规语言等价性的快速方法是Hopcroft和Karp算法(HK)。

这是算法的java实现: http://algs4.cs.princeton.edu/65reductions/HopcroftKarp.java.html