Grupos nombrados Regex en Java

173

Tengo entendido que el java.regexpaquete no tiene soporte para grupos con nombre ( http://www.regular-expressions.info/named.html ), así que ¿alguien puede señalarme hacia una biblioteca de terceros que sí lo tenga?

Miré a jregex pero su último lanzamiento fue en 2002 y no funcionó para mí (es cierto que solo lo intenté brevemente) en java5.

Dan
fuente
3
Tu comprensión es incorrecta. JDK7 maneja grupos con nombre.
tchrist
2
@tchrist En 2009 no había JDK7.
Alex78191

Respuestas:

276

( Actualización : agosto de 2011 )

Como geofflane menciona en su respuesta , Java 7 ahora admite grupos con nombre .
tchrist señala en el comentario que el apoyo es limitado.
Se detalla las limitaciones en su gran respuesta " Java Regex Helper "

El soporte de grupo denominado Java 7 regex se presentó en septiembre de 2010 en el blog de Oracle .

En el lanzamiento oficial de Java 7, las construcciones para soportar el grupo de captura nombrado son:

  • (?<name>capturing text) para definir un "nombre" de grupo con nombre
  • \k<name> hacer referencia a un "nombre" de grupo con nombre
  • ${name} para hacer referencia al grupo capturado en la cadena de reemplazo de Matcher
  • Matcher.group(String name) para devolver la subsecuencia de entrada capturada por el "grupo con nombre" dado.

Otras alternativas para pre-Java 7 fueron:


( Respuesta original : enero de 2009 , con los siguientes dos enlaces ahora rotos)

No puede hacer referencia al grupo con nombre, a menos que codifique su propia versión de Regex ...

Eso es precisamente lo que hizo Gorbush2 en este hilo .

Regex2

(implementación limitada, como lo señaló nuevamente tchrist , ya que solo busca identificadores ASCII. tchrist detalla la limitación como:

solo poder tener un grupo con nombre por el mismo nombre (¡sobre el cual no siempre tienes control!) y no poder usarlos para la recursividad en expresiones regulares.

Nota: Puede encontrar ejemplos de recursividad de expresiones regulares verdaderas en expresiones regulares de Perl y PCRE, como se menciona en Regexp Power , especificaciones de PCRE y diapositivas de Correspondencia con paréntesis equilibrados )

Ejemplo:

Cuerda:

"TEST 123"

RegExp:

"(?<login>\\w+) (?<id>\\d+)"

Acceso

matcher.group(1) ==> TEST
matcher.group("login") ==> TEST
matcher.name(1) ==> login

Reemplazar

matcher.replaceAll("aaaaa_$1_sssss_$2____") ==> aaaaa_TEST_sssss_123____
matcher.replaceAll("aaaaa_${login}_sssss_${id}____") ==> aaaaa_TEST_sssss_123____ 

(extracto de la implementación)

public final class Pattern
    implements java.io.Serializable
{
[...]
    /**
     * Parses a group and returns the head node of a set of nodes that process
     * the group. Sometimes a double return system is used where the tail is
     * returned in root.
     */
    private Node group0() {
        boolean capturingGroup = false;
        Node head = null;
        Node tail = null;
        int save = flags;
        root = null;
        int ch = next();
        if (ch == '?') {
            ch = skip();
            switch (ch) {

            case '<':   // (?<xxx)  look behind or group name
                ch = read();
                int start = cursor;
[...]
                // test forGroupName
                int startChar = ch;
                while(ASCII.isWord(ch) && ch != '>') ch=read();
                if(ch == '>'){
                    // valid group name
                    int len = cursor-start;
                    int[] newtemp = new int[2*(len) + 2];
                    //System.arraycopy(temp, start, newtemp, 0, len);
                    StringBuilder name = new StringBuilder();
                    for(int i = start; i< cursor; i++){
                        name.append((char)temp[i-1]);
                    }
                    // create Named group
                    head = createGroup(false);
                    ((GroupTail)root).name = name.toString();

                    capturingGroup = true;
                    tail = root;
                    head.next = expr(tail);
                    break;
                }
VonC
fuente
ambos enlaces anteriores parecen estar rotos?
Jonas
Este código tiene errores. Está buscando identificadores ASCII. Eso está mal. ¡Debería estar buscando cualquier cosa que Java permita en un identificador!
tchrist
1
Solo para tu información, ya que pareces tan concienzudo, la parte limitada no se trata tanto de los nombres ASCII vs Unicode como de solo poder tener un grupo con el mismo nombre (¡sobre el cual no siempre tienes control!) Y no poder usarlos para la recursividad en regex.
tchrist
@tchrist: gracias por esta precisión (incluida). También he agregado un enlace a su respuesta estelar en "Java Regex helper" (upvoted).
VonC
No hay un método matcher.name (int index) para el objeto Matcher en Java?
ot0
27

Sí, pero es desordenado hackear las clases de sol. Hay una forma más simple:

http://code.google.com/p/named-regexp/

named-regexp es un envoltorio delgado para la implementación de expresiones regulares JDK estándar, con el único propósito de manejar grupos de captura con nombre en el estilo .net: (? ...).

Se puede usar con Java 5 y 6 (se usan genéricos).

Java 7 manejará grupos de captura con nombre, por lo que este proyecto no está destinado a durar.

John Hardy
fuente
1
Lástima que esto no se pueda usar desde GWT.
Sakuraba
44
Echa un vistazo a la bifurcación GitHub de este proyecto, que corrige varios errores del original. También está alojado en Maven Central.
tony19
1
Solo una advertencia en mi caso, la bifurcación tony19 en Github no funciona en Android a partir de 0.1.8.
Chuck D
2
@RubberMallet, el problema específico de Android ahora está solucionado y estará en 0.1.9.
tony19
2

¿Qué tipo de problema tienes con jregex ? Me funcionó bien en java5 y java6.

Jregex hace bien el trabajo (incluso si la última versión es de 2002), a menos que desee esperar a Java 7 .

Brian Clozel
fuente
2

Para aquellos que ejecutan pre-java7, los grupos con nombre son compatibles con joni (puerto Java de la biblioteca de expresiones regulares Oniguruma ). La documentación es escasa, pero nos ha funcionado bien.
Los binarios están disponibles a través de Maven ( http://repository.codehaus.org/org/jruby/joni/joni/ ).

Ryan Smith
fuente
Estoy muy interesado en la opción joni mencionada anteriormente por Ryan: ¿tiene fragmentos de código que usen grupos de captura con nombre? He logrado que la coincidencia básica y la búsqueda funcionen correctamente, pero no veo qué método usaría para obtener acceso a los nombres de grupo o para obtener el valor de una captura utilizando el nombre del grupo.
malsmith
1

Una pregunta un poco vieja, pero me encontré necesitando esto también y que las sugerencias anteriores eran inadecuadas, y como tal, desarrollé un envoltorio delgado: https://github.com/hofmeister/MatchIt

Henrik Hofmeister
fuente