2012-04-07 24 views
7

¿Hay una manera fácil de comparar dos objetos Pattern?java comparando dos Objetos de patrón

Tengo un Pattern que se compiló usando la expresión regular "//" para comprobar si hay comentarios en un código.

Dado que hay varios regex para describir los comentarios, quiero encontrar una forma de diferenciarlos.

¿Cómo se puede hacer? la clase Pattern no implementa el método equals.

Respuesta

4

Quizás no entiendo completamente la pregunta. Pero como puede ver en el siguiente ejemplo, existe un método predeterminado java.lang.Object.equals(Object) para cada objeto Java. Este método compara las referencias a los objetos, es decir, utiliza el operador ==.

 

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

Salidas:

 

true 
false 
true 
true 
 
2

Pattern no lo hace pero String hace. ¿Por qué no simplemente comparar la expresión regular a partir de la cual se compilaron los Pattern s?

0

Puede comparar representaciones de cadena de la que han sido hechas patrones:

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

Es posible comparar Pattern objetos comparando el resultado de llamar pattern() o toString pero esto no hace lo que quieres (si entiendo tu pregunta correctamente). Específicamente, esto compara las cadenas que se pasaron al método de fábrica Pattern.compile(...).

No hay una manera simple de probar si dos expresiones regulares no idénticas son equivalentes. Por ejemplo, ".+" y "..*" representan expresiones regulares equivalentes, pero no hay una forma directa de determinar esto usando la API Pattern.

(I ni siquiera saben si el problema es solucionable teóricamente ... en el caso general.)


también quiero hacer comentarios sobre la respuesta aceptada. El autor proporciona un código que afirma que muestra que el método equals de Pattern se hereda de Object. De hecho, la salida que está viendo es consistente con eso ... pero no muestra.

La forma correcta de saber si este es el caso es mirar el javadoc ... donde el método equals figura en la lista de métodos heredados. Eso es definitivo.

Entonces, ¿por qué el ejemplo no muestra lo que el autor dice que muestra?

  1. Es posible que dos métodos se comporten de la misma manera pero se implementen de manera diferente. Si tratamos la clase Pattern como una caja negra, entonces no podemos mostrar que esto no está sucediendo. (O al menos ... no sin usar el reflejo.)

  2. El autor solo ha ejecutado esto en una plataforma. Otras plataformas podrían comportarse de manera diferente.

Sobre el segundo punto, lo que recuerdo es que en la ejecución anterior de Pattern (en Java 1.4) los métodos Pattern.compile(...) mantienen una caché de objetos compilados recientemente patrón . Si compiló una cadena de patrón en particular dos veces, la segunda vez puede obtener el mismo objeto que se devolvió la primera vez. Eso causaría la salida del código de prueba:

true 
    true 
    true 
    true 

Pero, ¿qué significa eso? ¿Muestra que Pattern prevalece sobre Object.equals? ¡No!

La lección aquí es que usted debe averiguar cómo se comporta un método de biblioteca de Java principalmente mirando las javadocs:

  • Si se escribe una prueba de "recuadro negro", usted es responsable de dibujar conclusiones incorrectas ... o al menos, conclusiones que pueden no ser ciertas para todas las plataformas.

  • Si basa sus conclusiones en "leer el código", corre el riesgo de extraer conclusiones que no son válidas para otras plataformas.


1 - Incluso si mi memoria no es correcta, tal implementación sería consistente con los javadocs para los métodos Pattern.compile(...). No dicen que cada llamada compile devuelve un nuevo objeto Pattern.

+0

Si se compara 'pattern' se perderá las banderas. –

+0

@MarkusMalkusch - Sí, esa es otra razón por la que comparar objetos 'Pattern' con' pattern() 'podría ser problemático. –

+0

Los objetos de patrón nunca se han guardado automáticamente en caché. Como evidencia, los documentos API advierten que 'Pattern.matches()' y 'String # matches()' no permiten que el objeto Pattern se reutilice, por lo que no deberían usarse para llamadas repetidas, como en un bucle. (La clase Scanner * does * almacena en caché todos los patrones que utiliza, pero eso se maneja internamente). –

3

Por razones misteriosas, el objeto Pattern no implementa equals(). Por ejemplo, este sencillo unittest fallará:

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

La solución más común para este parece ser el de comparar las representaciones de cadena de los objetos del modelo (que devuelve la cadena que se utiliza para crear el patrón):

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

Creo que tengo la idea de la pregunta y desde que busqué la forma de comparar Pattern s termino aquí (dos años demasiado tarde, probablemente, bueno, lo siento ...).

Estoy escribiendo pruebas y necesito saber si un método mío devuelve el patrón esperado. Si bien el texto a través de toString() o pattern() podría ser el mismo, los indicadores pueden ser diferentes y el resultado al usar el patrón sería inesperado.

Hace un tiempo escribí mi propia implementación general de toString(). Recoge todos los campos, incluidos los private, y construye una cadena que se puede usar para iniciar sesión y aparentemente para realizar pruebas. Mostró que los campos root y matchRoot eran diferentes al compilar dos patrones iguales. Suponiendo que esos dos no son tan relevantes para la igualdad y dado que hay un campo flag, mi solución es bastante buena si no perfecta.

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

Y la prueba es de color verde:

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

para determinar si dos Pattern objetos son equivalentes, lo más sencillo que hacer es comparar el patrón de cadena real y las banderas utilizado para crear ese patrón:

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

Sé que el autómata puede resolver su problema. Pero eso tal vez complicado. Aproximadamente, debe comparar pattern.pattern() y pattern.flags() al menos, aunque no es suficiente para decidir si dos expresiones regulares son equivalentes o no.

0

Aunque las otras respuestas podrían resolver el problema, no creo que sean la respuesta real al problema.

Si realmente desea comparar dos patrones, esencialmente desea comparar dos idiomas regulares.

Para ello, cs StackExchange ya se ha publicado una solución: https://cs.stackexchange.com/questions/12876/equivalence-of-regular-expressions

Un método rápido para comprobar la equivalencia de los lenguajes regulares es el algoritmo Hopcroft y Karp (HK).

Aquí es una implementación de Java del algoritmo: http://algs4.cs.princeton.edu/65reductions/HopcroftKarp.java.html