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方法列表中。 这是确定的。
那么为什么示例不能显示作者所说的内容呢?
-
两种方法可能以相同的方式运行,但可以采用不同的方式实现。 如果我们将
Pattern
类视为黑盒子,那么我们就无法certificate这不会发生。 (或者至少……不是没有使用reflection。) -
作者只在一个平台上运行它。 其他平台可能表现不同。
在第二点,我的回忆是在早期的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
字段,并构造一个字符串,可用于记录并显然用于测试。 它表明,在编译两个相同的模式时,字段root
和matchRoot
是不同的。 假设这两者与平等无关,并且因为有一个字段flag
,我的解决方案如果不完美则相当好。
/** * Don't call this method from a
toString()
method with *useExistingToString
set totrue
!!! */ 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) { } } ListignoreFieldNameList = 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