Regex para etiquetas específicas y su contenido, agrupadas por el nombre de la etiqueta

StackOverflow https://stackoverflow.com/questions/200525

  •  03-07-2019
  •  | 
  •  

Pregunta

Aquí está la entrada (html, no xml):

... html content ...
<tag1> content for tag 1 </tag1>
<tag2> content for tag 2 </tag2>
<tag3> content for tag 3 </tag3>
... html content ...

Me gustaría obtener 3 coincidencias, cada una con dos grupos. El primer grupo contendría el nombre de la etiqueta y el segundo grupo contendría el texto interno de la etiqueta. Solo hay esas tres etiquetas, por lo que no es necesario que sea universal.

En otras palabras:

match.Groups["name"] would be "tag1"
match.Groups["value"] would be "content for tag 2"

¿Alguna idea?

¿Fue útil?

Solución

No veo por qué querrías usar nombres de grupos de coincidencias para eso.

Aquí hay una expresión regular que coincidiría con el nombre de la etiqueta y el contenido de la etiqueta en subpartidas numeradas.

<(tag1|tag2|tag3)>(.*?)</$1>

Aquí hay una variante con nombres de grupos de estilo .NET

<(?'name'tag1|tag2|tag3)>(?'value'.*?)</\k'name'>.

EDITAR

RegEx adaptado según la aclaración del autor de la pregunta.

Otros consejos

Regex para esto podría ser:

/<([^>]+)>([^<]+)<\/\1>/

Pero es general, ya que no sé mucho sobre el mecanismo de escape de .NET. Para traducirlo:

  • primer grupo coincide con el nombre de la primera etiqueta entre < y >
  • el segundo grupo coincide con el contenido (de > al siguiente <
  • la comprobación final si la primera etiqueta está cerrada

HTH

Gracias a todos, pero ninguna de las expresiones regulares funciona. :( Tal vez no fui lo suficientemente específico, lo siento. Aquí está el html exacto que estoy tratando de analizar:

...some html content <b> etc </b> ...
<user> hello <b>mitch</b> </user>
...some html content <b> etc </b> ...
<message> some html <i>message</i> <a href....>bla</a> </message>
...some html content <b> etc </b> ...

Espero que esté más claro ahora. Estoy detrás de las etiquetas USER y MESSAGE.

Necesito obtener dos coincidencias, cada una con dos grupos. El primer grupo me daría el nombre de la etiqueta (usuario o mensaje) y el segundo grupo me daría el texto interno completo de la etiqueta.

¿Los datos son correctos xml, o simplemente se ven así?

Si es html, vale la pena investigar el Paquete de agilidad HTML : esto proporciona un DOM ( similar a XmlDocument) que puede usar para consultar los datos:

string input = @"<html>...some html content <b> etc </b> ...
<user> hello <b>mitch</b> </user>
...some html content <b> etc </b> ...
<message> some html <i>message</i> <a href....>bla</a> </message>
...some html content <b> etc </b> ...</html>";

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(input);
            foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//user | //message"))
            {
                Console.WriteLine("{0}: {1}", node.Name, node.InnerText);
                // or node.InnerHtml to keep the formatting within the content
            }

Esto genera:

user:  hello mitch
message:  some html message bla

Si desea las etiquetas de formato, use .InnerHtml en lugar de .InnerText.

Si es xml, entonces para codificar con el espectro completo de xml, sería mejor usar un analizador xml. Para xml de tamaño pequeño a mediano, cargarlo en un DOM como XmlDocument estaría bien, luego consultar los nodos (por ejemplo, & Quot; // * & Quot;). Para xml enorme, XmlReader podría ser una opción.

Si los datos no tienen que preocuparse por el xml completo, entonces una expresión regular simple no debería ser demasiado complicada ... un ejemplo simplificado (sin atributos, sin espacios de nombres, sin xml anidado) podría ser:

string input = @"blah <tag1> content for tag 1 </tag1> blop
<tag2> content for tag 2 </tag2> bloop
<tag3> content for tag 3 </tag3> blip";

        const string pattern = @"<(\w+)>\s*([^<>]*)\s*</(\1)>";
        Console.WriteLine(Regex.IsMatch(input, pattern));
        foreach(Match match in Regex.Matches(input, pattern)) {
            Console.WriteLine("{0}: {1}", match.Groups[1], match.Groups[2]);
        }

El problema era que las personas ([^ <] *) que usaban para unir elementos dentro de las etiquetas coincidían con la apertura < de las etiquetas anidadas, y luego la etiqueta de cierre de la etiqueta anidada no coincidía con la etiqueta externa y, por lo tanto, la expresión regular falló.

Aquí hay una versión un poco más robusta de la expresión regular de Tomalak que permite atributos y espacios en blanco:

Regex tagRegex = new Regex(@"<\s*(?<tag>" + string.Join("|", tags) + @")[^>]*>(?<content>.*?)<\s*/\s*\k<tag>\s*>", RegexOptions.IgnoreCase);

Obviamente, si solo necesita usar un conjunto específico de etiquetas, puede reemplazar el

string.Joing("|", tags)

con la lista de etiquetas separadas de tubería codificada.

Las limitaciones de la expresión regular son que si tiene una etiqueta que intenta hacer coincidir anidada dentro de otra, solo coincidirá con la etiqueta externa. es decir

  

< usuario > abc < mensaje > def < / mensaje > ghi < / usuario >

Coincidirá con la etiqueta de usuario externa, pero no con la etiqueta de mensaje interna.

Tampoco maneja los atributos de > citados de la siguiente manera:

  

< user attrib = " oops > " >

Simplemente coincidirá

  

< user attrib = " oops >

como la etiqueta y el

  

" >

formará parte del contenido de las etiquetas.

Esto le dará grupos de captura con nombre para lo que desea. Sin embargo, no funcionará para etiquetas anidadas.

/<(?<name>[^>]+)>(?<value>[^<]+)</\1>/

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top