2012-04-07 17 views
7

Gibt es eine einfache Möglichkeit, zwei Pattern Objekte zu vergleichen?Java vergleicht zwei Musterobjekte

Ich habe eine Pattern, die mit der Regex "//" kompiliert, um nach Kommentaren in einem Code zu überprüfen.

Da es mehrere Regex gibt, um Kommentare zu beschreiben, möchte ich einen Weg finden, sie zu unterscheiden.

Wie kann es gemacht werden? Die Klasse Pattern implementiert die Methode equals nicht.

Antwort

4

Vielleicht verstehe ich die Frage nicht vollständig. Aber wie Sie im folgenden Beispiel sehen können, gibt es für jedes Java-Objekt eine Standardmethode java.lang.Object.equals(Object). Diese Methode vergleicht die Referenzen zu den Objekten, d.h. verwendet den Operator ==.

 

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())); 
    } 
} 
 

Ausgänge:

 

true 
false 
true 
true 
 
2

Pattern tut nicht aber String tut. Warum nicht einfach die Regex vergleichen, aus der die Pattern s kompiliert wurden?

0

Sie können Stringdarstellungen vergleichen, aus dem Muster gemacht wurden:

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

Sie Pattern Objekte durch Vergleich des Ergebnisses des Aufrufs pattern() oder toString vergleichen kann, aber das macht nicht was du willst (wenn ich deine Frage richtig verstanden habe). Dies vergleicht insbesondere die Zeichenfolgen, die an die Factory-Methode Pattern.compile(...) übergeben wurden.

Es gibt keine einfache Möglichkeit zu testen, ob zwei nicht identische Regexes äquivalent sind. Zum Beispiel stellen ".+" und "..*" äquivalente Regexes dar, aber es gibt keine direkte Möglichkeit, dies unter Verwendung der Pattern API zu bestimmen.

(ich weiß nicht einmal, ob das Problem theoretisch auflösbar ... im allgemeinen Fall ist.)


ich auf der akzeptierte Antwort auch kommentieren will. Der Autor stellt einen Code zur Verfügung, den er behauptet, zeigt, dass equals Pattern von Object vererbt wird. In der Tat ist die Ausgabe, die er sieht, konsistente mit dem ... aber es zeigt nicht zeigen es.

Der richtige Weg, um zu wissen, ob dies der Fall ist, ist die Javadoc ... wo die equals Methode in der Liste der geerbten Methoden aufgeführt ist. Das ist definitiv.

Warum zeigt das Beispiel nicht, was der Autor sagt?

  1. Es ist möglich, dass zwei Methoden, um die gleiche Art und Weise zu verhalten, aber anders umgesetzt werden. Wenn wir die Klasse Pattern als Blackbox behandeln, können wir nicht zeigen, dass dies nicht geschieht. (Oder zumindest ... nicht ohne Reflexion.)

  2. Der Autor hat dies nur auf einer Plattform ausgeführt. Andere Plattformen könnten sich anders verhalten.

Am zweiten Punkt ist meine Erinnerung, dass die in der früheren Umsetzung Pattern (in Java 1.4) Pattern.compile(...) Verfahren ein Cache von kürzlich kompilierten Mustern aufbewahrten Objekte. Wenn Sie eine bestimmte Musterzeichenfolge zweimal kompiliert haben, erhalten Sie beim zweiten Mal möglicherweise das gleiche Objekt, das beim ersten Mal zurückgegeben wurde. Das würde den Testcode ausgeben:

true 
    true 
    true 
    true 

Aber was zeigt das? Wird angezeigt, dass Pattern die Object.equals überschreibt? Nein!

Die Lektion hier ist, dass Sie herausfinden sollte, wie eine Java-Bibliothek Methode in erster Linie, indem man die javadocs verhält:

  • Wenn Sie eine „black box“ Test schreiben, sind Sie verantwortlich zu zeichnen falsche Schlussfolgerungen ... oder zumindest Schlussfolgerungen, die möglicherweise nicht für alle Plattformen zutreffen.

  • Wenn Sie Ihre Schlussfolgerungen auf "Lesen des Codes" stützen, laufen Sie Gefahr, Schlussfolgerungen zu ziehen, die für andere Plattformen ungültig sind.


1 - Auch wenn meine Erinnerung nicht richtig ist, eine solche Implementierung würde mit den javadocs für die Pattern.compile(...) Methoden konsistent sein. Sie sagen nicht, dass jeder Aufruf compile ein neues Objekt Pattern zurückgibt.

+0

Wenn Sie vergleichen 'pattern' Sie die Flaggen verpassen. –

+0

@MarkusMalkusch - Ja, das ist ein weiterer Grund, warum das Vergleichen von 'Pattern' Objekten mit' pattern() 'problematisch sein könnte. –

+0

Musterobjekte wurden nie automatisch zwischengespeichert. Als Beweis dafür warnt die API-Dokumentation, dass 'Pattern.matches()' und 'String # matches()' das Pattern-Objekt nicht wiederverwenden und daher nicht für wiederholte Aufrufe wie in einer Schleife verwendet werden sollten. (Die Scanner-Klasse * speichert alle Muster, die sie verwendet, jedoch intern.) –

3

Aus mysteriösen Gründen implementiert das Musterobjekt nicht gleich(). Zum Beispiel wird dieser einfache Unittest fehlschlagen:

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

Die häufigste Abhilfe für diese scheint die Zeichenfolge Darstellungen der Pattern-Objekte zu vergleichen (die den String zurückgibt verwendet, um das Muster zu erstellen):

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

Ich denke, ich bekomme die Idee von der Frage, und da ich nach Möglichkeiten suchte zu vergleichen Pattern s Ich lande hier (zwei Jahre zu spät wahrscheinlich, naja, tut mir leid ...).

Ich schreibe Tests und ich muss wissen, ob eine Methode von mir das erwartete Muster zurückgibt. Während der Text über toString() oder pattern() identisch sein kann, können die Flags unterschiedlich sein und das Ergebnis bei Verwendung des Musters wäre unerwartet.

Vor einiger Zeit habe ich meine eigene allgemeine Implementierung von toString() geschrieben. Es sammelt alle Felder einschließlich der private Einsen und erstellt eine Zeichenfolge, die zum Protokollieren und anscheinend zum Testen verwendet werden kann. Es zeigte, dass die Felder root und matchRoot beim Kompilieren zweier gleicher Muster unterschiedlich waren. Unter der Annahme, dass diese beiden für die Gleichheit nicht relevant sind und da es ein Feld flag gibt, ist meine Lösung ziemlich gut, wenn nicht perfekt.

/** 
* 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() + "'"; 
} 

und der Test ist grün:

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); 
0

Um zu bestimmen, ob zwei Pattern Objekte äquivalent sind, die einfachste Sache zu tun, um die tatsächliche Bespannbild und die Fahnen erstellen, um zu vergleichen, dass Muster:

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

Ich weiß, dass Automaten Ihr Problem lösen können. Aber das ist vielleicht kompliziert. Grob sollte man zumindest pattern.pattern() und pattern.flags() vergleichen, aber es ist nicht genug zu entscheiden, ob zwei Regex gleichwertig sind oder nicht.

0

Obwohl die anderen Antworten das Problem lösen könnten, glaube ich nicht, dass sie die wirkliche Antwort auf das Problem sind.

Wenn Sie wirklich zwei Muster vergleichen möchten Sie im Wesentlichen zwei reguläre Sprachen vergleichen.

Um dies zu tun, hat cs Stack veröffentlicht bereits eine Lösung: https://cs.stackexchange.com/questions/12876/equivalence-of-regular-expressions

Eine schnelle Methode, die Gleichwertigkeit der regulären Sprachen zu überprüfen, die Hopcroft und Karp Algorithmus (HK) ist.

Hier ist eine Java-Implementierung des Algorithmus: http://algs4.cs.princeton.edu/65reductions/HopcroftKarp.java.html

Verwandte Themen