Java regex para extraer texto entre etiquetas

82

Tengo un archivo con algunas etiquetas personalizadas y me gustaría escribir una expresión regular para extraer la cadena entre las etiquetas. Por ejemplo, si mi etiqueta es:

[customtag]String I want to extract[/customtag]

¿Cómo escribiría una expresión regular para extraer solo la cadena entre las etiquetas? Este código parece un paso en la dirección correcta:

Pattern p = Pattern.compile("[customtag](.+?)[/customtag]");
Matcher m = p.matcher("[customtag]String I want to extract[/customtag]");

No estás seguro de qué hacer después. ¿Algunas ideas? Gracias.

b10hazard
fuente
1
Para empezar, debe escapar de los []corchetes que son metacaracteres en una expresión regular.
ridgerunner

Respuestas:

184

Estás en el camino correcto. Ahora solo necesita extraer el grupo deseado, de la siguiente manera:

final Pattern pattern = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);
final Matcher matcher = pattern.matcher("<tag>String I want to extract</tag>");
matcher.find();
System.out.println(matcher.group(1)); // Prints String I want to extract

Si desea extraer varios hits, intente esto:

public static void main(String[] args) {
    final String str = "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear</tag>";
    System.out.println(Arrays.toString(getTagValues(str).toArray())); // Prints [apple, orange, pear]
}

private static final Pattern TAG_REGEX = Pattern.compile("<tag>(.+?)</tag>", Pattern.DOTALL);

private static List<String> getTagValues(final String str) {
    final List<String> tagValues = new ArrayList<String>();
    final Matcher matcher = TAG_REGEX.matcher(str);
    while (matcher.find()) {
        tagValues.add(matcher.group(1));
    }
    return tagValues;
}

Sin embargo, estoy de acuerdo en que las expresiones regulares no son la mejor respuesta aquí. Usaría XPath para encontrar elementos que me interesen. Consulte La API de Java XPath para obtener más información.

hoipolloi
fuente
3
Muchas gracias, eso es justo lo que necesitaba. Examinaré XPaths, pero por ahora creo que esta solución funcionará. Mis aplicaciones son muy simples y probablemente seguirán siendo así. ¡Gracias de nuevo!
b10hazard
¿Y esta cuerda "<tag>apple</tag><b>hello</b><tag>orange</tag><tag>pear"? ¿Cómo podemos conseguirlo pearsin la etiqueta de cierre?
K.Sopheak
Para generalizar: private String extractDataFromTags (etiqueta de cadena) {Pattern pattern = Pattern.compile ("<. +?> (. +?) </.+?>"); Matcher matcher = patrón.matcher (etiqueta); matcher.find (); return (matcher.group (1)); // Imprime la cadena que quiero extraer o lanza una excepción}
PMateus
15

Para ser sincero, las expresiones regulares no son la mejor idea para este tipo de análisis. La expresión regular que publicó probablemente funcionará muy bien para casos simples, pero si las cosas se vuelven más complejas, tendrá grandes problemas (la misma razón por la que no puede analizar HTML de manera confiable con expresiones regulares). Sé que probablemente no quieras escuchar esto, sé que no lo hice cuando hice el mismo tipo de preguntas, pero el análisis de cadenas se volvió MUCHO más confiable para mí después de que dejé de intentar usar expresiones regulares para todo.

jTopas es un tokenizador IMPRESIONANTE que hace que sea bastante fácil escribir analizadores a mano (SUGiero ENCARECIDAMENTE jtopas sobre las bibliotecas estándar de escáner java / etc. Si desea ver jtopas en acción, aquí hay algunos analizadores que escribí usando jTopas para analizar este tipo de archivo

Si está analizando archivos XML, debería utilizar una biblioteca de analizador xml. No lo haga usted mismo a menos que lo haga por diversión, hay muchas opciones probadas por ahí

jdc0589
fuente
Gracias por la sugerencia. Los he marcado como favoritos y ciertamente consideraré usar esto en proyectos futuros. Por ahora, el método de expresiones regulares es probablemente el que usaré, ya que el archivo que estoy analizando es muy pequeño / simple.
b10hazard
7

Un enfoque genérico, más simple y un poco primitivo para encontrar etiquetas, atributos y valores

    Pattern pattern = Pattern.compile("<(\\w+)( +.+)*>((.*))</\\1>");
    System.out.println(pattern.matcher("<asd> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd TEST</asd>").find());
    System.out.println(pattern.matcher("<asd attr='3'> TEST</asd>").find());
    System.out.println(pattern.matcher("<asd> <x>TEST<x>asd>").find());
    System.out.println("-------");
    Matcher matcher = pattern.matcher("<as x> TEST</as>");
    if (matcher.find()) {
        for (int i = 0; i <= matcher.groupCount(); i++) {
            System.out.println(i + ":" + matcher.group(i));
        }
    }
Gorky
fuente
¿Cuál sería el patrón si hay una secuencia de etiquetas diferentes o etiquetas anidadas como <h2>Mac</h2><h1>loves it</h1>o <h2>Mac<h1>liked your answer</h1></h2>?
MAC
1
edite i <matcher.groupCount (); para i <= matcher.groupCount (); para incluir la primera subcadena coincidente, es decir. en el índice 0
AVA
4

Prueba esto:

Pattern p = Pattern.compile(?<=\\<(any_tag)\\>)(\\s*.*\\s*)(?=\\<\\/(any_tag)\\>);
Matcher m = p.matcher(anyString);

Por ejemplo:

String str = "<TR> <TD>1Q Ene</TD> <TD>3.08%</TD> </TR>";
Pattern p = Pattern.compile("(?<=\\<TD\\>)(\\s*.*\\s*)(?=\\<\\/TD\\>)");
Matcher m = p.matcher(str);
while(m.find()){
   Log.e("Regex"," Regex result: " + m.group())       
}

Salida:

10 ene

3,08%

Heriberto Rivera
fuente
2
    final Pattern pattern = Pattern.compile("tag\\](.+?)\\[/tag");
    final Matcher matcher = pattern.matcher("[tag]String I want to extract[/tag]");
    matcher.find();
    System.out.println(matcher.group(1));
Bibhuti Agarwal
fuente
¿Qué tal el prefijo para la etiqueta (si el prefijo es dinámico)?
user1514499
2
    String s = "<B><G>Test</G></B><C>Test1</C>";

    String pattern ="\\<(.+)\\>([^\\<\\>]+)\\<\\/\\1\\>";

       int count = 0;

        Pattern p = Pattern.compile(pattern);
        Matcher m =  p.matcher(s);
        while(m.find())
        {
            System.out.println(m.group(2));
            count++;
        }
Shubham Khurana
fuente
1

Prefijo esta respuesta con "no debe usar una expresión regular para analizar XML; solo dará como resultado casos extremos que no funcionan correctamente y una expresión regular de complejidad creciente mientras intenta solucionarlo . "

Dicho esto, debe continuar haciendo coincidir la cadena y tomando el grupo que desea:

if (m.matches())
{
   String result = m.group(1);
   // do something with result
}
Shirik
fuente