java比较两个Pattern对象

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

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

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

如何做呢? Pattern类不实现equals方法。

也许我不完全理解这个问题。 但正如您在下面的示例中所看到的,每个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())); } }
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
true false true true 

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

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

(我甚至不知道这个问题在理论上是否可以解决……在一般情况下。)


我还想对接受的答案发表评论。 作者提供了一些他声称显示 Pattern的equals方法inheritance自Object 。 事实上,他所看到的输出与…… 一致 ……但它没有显示出来。

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

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

  1. 两种方法可能以相同的方式运行,但可以采用不同的方式实现。 如果我们将Pattern类视为黑盒子,那么我们就无法certificate这不会发生。 (或者至少……不是没有使用reflection。)

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

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

  true true true true 

但这表明了什么? 它是否表明Pattern覆盖了Object.equals ? 没有!

这里的教训是,您应该首先通过查看javadoc来弄清楚Java库方法的行为:

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

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


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

出于神秘的原因,Pattern对象不实现equals()。 例如,这个简单的unit testing将失败:

  @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! } 

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

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

您可以比较已生成模式的字符串表示forms:

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

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

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

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

 /** * Don't call this method from a toString() method with * useExistingToString set to true!!! */ public static String toString(Object object, boolean useExistingToString, String... ignoreFieldNames) { if (object == null) { return null; } Class 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 ignoreFieldNameList = Arrays.asList(ignoreFieldNames); Map fields = new HashMap(); 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); 

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

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

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

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

为此,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