¿Puede proporcionar algunos ejemplos de por qué es difícil analizar XML y HTML con una expresión regular? [cerrado]

402

Un error que veo que la gente comete una y otra vez es tratar de analizar XML o HTML con una expresión regular. Estas son algunas de las razones por las que analizar XML y HTML es difícil:

La gente quiere tratar un archivo como una secuencia de líneas, pero esto es válido:

<tag
attr="5"
/>

La gente quiere tratar la etiqueta <o <como el comienzo de una etiqueta, pero cosas como esta existen en la naturaleza:

<img src="imgtag.gif" alt="<img>" />

La gente a menudo quiere hacer coincidir las etiquetas iniciales con las finales, pero XML y HTML permiten que las etiquetas se contengan a sí mismas (que las expresiones regulares tradicionales no pueden manejar en absoluto):

<span id="outer"><span id="inner">foo</span></span> 

Las personas a menudo quieren comparar con el contenido de un documento (como el famoso problema "encontrar todos los números de teléfono en una página determinada"), pero los datos pueden estar marcados (incluso si parece ser normal cuando se ve):

<span class="phonenum">(<span class="area code">703</span>)
<span class="prefix">348</span>-<span class="linenum">3020</span></span>

Los comentarios pueden contener etiquetas mal formateadas o incompletas:

<a href="foo">foo</a>
<!-- FIXME:
    <a href="
-->
<a href="bar">bar</a>

¿De qué otras cosas te das cuenta?

Chas Owens
fuente
14
Los navegadores web tienen sentido de este tipo de desastre millones de veces por segundo, ¿alguien puede crear una clase de analizador de páginas web para nosotros, simples mortales?
Jon Winstanley
24
Jon, tienen. En Perl hay muchos HTML :: Parser, HTML :: TreeBuilder, etc. Es casi seguro que haya uno para su idioma.
Chas. Owens
12
La mejor respuesta es stackoverflow.com/a/1732454/135078 (Cuidado con Zalgo)
Kelly S. French,
3
Hay una buena explicación de por qué [no puede analizar [X] HTML con expresiones regulares] [1] [1]: stackoverflow.com/a/1732454/468725
Pavel P
44
Aquí hay una buena explicación de cómo ciertamente puede analizar HTML con patrones , así como por qué probablemente no desea hacerlo.
tchrist

Respuestas:

260

Aquí hay un divertido XML válido para ti:

<!DOCTYPE x [ <!ENTITY y "a]>b"> ]>
<x>
    <a b="&y;>" />
    <![CDATA[[a>b <a>b <a]]>
    <?x <a> <!-- <b> ?> c --> d
</x>

Y este pequeño paquete de alegría es HTML válido:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" [
    <!ENTITY % e "href='hello'">
    <!ENTITY e "<a %e;>">
]>
    <title>x</TITLE>
</head>
    <p id  =  a:b center>
    <span / hello </span>
    &amp<br left>
    <!---- >t<!---> < -->
    &e link </a>
</body>

Sin mencionar todo el análisis específico del navegador para construcciones no válidas.

¡Buena suerte enfrentando expresiones regulares contra eso!

EDITAR (Jörg W Mittag): Aquí hay otra buena pieza de HTML 4.01 válido y bien formado:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>
bobince
fuente
66
El XML? Hay algunas construcciones diferentes allí, lo cual es problemático. El subconjunto interno DTD? Eso es definir una nueva & entidad; llamado 'y', que contiene una secuencia ']>' que normalmente, si no entre comillas, finalizaría el subconjunto interno.
bobince 01 de
16
(Esto demuestra que usted tiene que tener conocimiento bastante profundo sobre algunas de las DTD más esotérico y arcaico características de XML para analizar un documento correctamente, incluso si usted no es un analizador DTD-validación.)
bobince
17
Los ejemplos HTML hacen uso de una característica rara vez conocida: etiquetas cortas. Lea más en w3.org/QA/2007/10/shorttags.html
netvope
25
Cada vez que alguien escribe HTML como se muestra arriba, Tim Berners-Lee derrama una sola lágrima.
fgysin reinstala a Monica el
55
Me encanta cómo el resaltador de sintaxis de Stackoverflow falla en la primera aparición de "]".
GlassGhost
71

Realmente

<img src="imgtag.gif" alt="<img>" />

no es HTML válido y tampoco es XML válido.

No es XML válido porque '<' y '>' no son caracteres válidos dentro de las cadenas de atributos. Deben escapar utilizando las entidades XML correspondientes & lt; y & gt;

Tampoco es HTML válido porque el formulario de cierre corto no está permitido en HTML (pero es correcto en XML y XHTML). La etiqueta 'img' también es una etiqueta implícitamente cerrada según la especificación HTML 4.01. Esto significa que cerrarlo manualmente es realmente incorrecto y es equivalente a cerrar cualquier otra etiqueta dos veces.

La versión correcta en HTML es

<img src="imgtag.gif" alt="&lt;img&gt;">

y la versión correcta en XHTML y XML es

<img src="imgtag.gif" alt="&lt;img&gt;"/>

El siguiente ejemplo que diste también es inválido

<
tag
attr="5"
/>

Esto tampoco es HTML o XML válido. El nombre de la etiqueta debe estar justo detrás de '<', aunque los atributos y el cierre '>' pueden estar donde quieran. Entonces el XML válido es en realidad

<tag
attr="5"
/>

Y aquí hay otro más funky: en realidad puede elegir usar "o" como su carácter de cita de atributo

<img src="image.gif" alt='This is single quoted AND valid!'>

Todas las otras razones que se publicaron son correctas, pero el mayor problema al analizar HTML es que las personas generalmente no entienden todas las reglas de sintaxis correctamente. El hecho de que su navegador interprete su tagoup como HTML no significa que haya escrito HTML válido.

Editar: e incluso stackoverflow.com está de acuerdo conmigo con respecto a la definición de válido e inválido. Su XML / HTML no válido no está resaltado, mientras que mi versión corregida sí.

Básicamente, XML no está hecho para ser analizado con expresiones regulares. Pero tampoco hay razón para hacerlo. Hay muchos, muchos analizadores XML para cada idioma. Puede elegir entre analizadores SAX, analizadores DOM y analizadores Pull. Se garantiza que todo esto será mucho más rápido que el análisis con una expresión regular y luego puede usar tecnologías geniales como XPath o XSLT en el árbol DOM resultante.

Por lo tanto, mi respuesta es: no solo analizar XML con regexps es difícil, sino que también es una mala idea. Simplemente use uno de los millones de analizadores XML existentes y aproveche todas las características avanzadas de XML.

HTML es demasiado difícil incluso intentar analizarlo usted mismo. Primero, la sintaxis legal tiene muchas pequeñas sutilezas de las que quizás no estés al tanto, y segundo, HTML en la naturaleza es solo una enorme pila apestosa (me entiendes). Hay una variedad de bibliotecas de analizadores laxas que hacen un buen trabajo al manejar HTML como la sopa de etiquetas, solo utilícelas.

LordOfThePigs
fuente
8
Sin embargo, no necesitas escapar> como>.
Joey
8
Bien, s / valid / existe en la naturaleza / g
Chas. Owens
1
En realidad, de acuerdo con la especificación, debe escapar> como> tal como debe escapar <como <& y & amp; y en los atributos "as & quot; y 'as & apos; es solo ese analizador sintáctico
LordOfThePigs
19
La especificación no dice que '>' debe escaparse, excepto en el caso especial de la secuencia ']]>' en el contenido. Por esta razón, es más fácil escapar siempre '>', pero no es requerido por la especificación.
bobince
8
>el signo es perfectamente válido en html stackoverflow.com/questions/94528/…
jfs el
56

Escribí una entrada de blog completa sobre este tema: Limitaciones de expresión regular

El quid de la cuestión es que HTML y XML son estructuras recursivas que requieren mecanismos de recuento para analizar correctamente. Una verdadera expresión regular no es capaz de contar. Debe tener una gramática libre de contexto para contar.

El párrafo anterior viene con una leve advertencia. Ciertas implementaciones de expresiones regulares ahora admiten la idea de recursividad. Sin embargo, una vez que comience a agregar recursividad a sus expresiones regulares, realmente está extendiendo los límites y debería considerar un analizador sintáctico.

JaredPar
fuente
20

Una cosa que no está en su lista es que los atributos pueden aparecer en cualquier orden, por lo que si su expresión regular está buscando un enlace con el href "foo" y la clase "bar", pueden aparecer en cualquier orden y tener cualquier otro número cosas entre ellos

AmbroseChapel
fuente
Ah, sí, esa fue incluso la pregunta que me llevó a hacer esta (el primer enlace).
Chas. Owens
16

Depende de lo que quieras decir con "análisis". En términos generales, XML no se puede analizar utilizando expresiones regulares, ya que la gramática XML no es regular. En pocas palabras, las expresiones regulares no pueden contar (bueno, las expresiones regulares de Perl en realidad podrían contar cosas), por lo que no puede equilibrar las etiquetas de apertura-cierre.

Anton Gogolev
fuente
Supongo que las referencias inversas pueden resolver el problema de abrir y cerrar etiquetas
Rishul Matta 03 de
1
@RishulMatta: ¿cómo? Solo tiene un número limitado de referencias inversas y tenga en cuenta que debe invertir las etiquetas ... Además, la definición estricta de expresiones regulares no permite referencias inversas.
Willem Van Onsem
.NET permite equilibrar expresiones, que aparecen y empujan, y teóricamente podría usarse para hacer coincidir la jerarquía. Pero sigue siendo una mala idea.
Abel
9

¿Las personas realmente están cometiendo un error al usar una expresión regular, o simplemente es lo suficientemente bueno para la tarea que están tratando de lograr?

Estoy totalmente de acuerdo en que analizar html y xml usando una expresión regular no es posible, ya que otras personas han respondido.

Sin embargo, si su requisito no es analizar html / xml, sino obtener un pequeño bit de datos en un bit "html / xml" conocido "bueno, entonces tal vez una expresión regular o incluso una" subcadena "aún más simple sea suficiente.

Día de robin
fuente
77
Definir "lo suficientemente bueno". Inevitablemente, la expresión regular simple no funcionará. ¿No coincide con algo o con algo que no debería ser un error? Si es así, usar expresiones regulares es un error. Los analizadores HTML y XML no son difíciles de usar. Evitar aprenderlos es una economía falsa.
Chas. Owens
1
ok, define "suficientemente bueno". Digamos que tengo una página web que me dice la dirección IP del cliente. Eso es todo lo que hace. Ahora, necesito escribir una aplicación para la máquina del cliente que me indique su dirección IP. Voy a ese sitio, busco una dirección IP y la devuelvo. ¡No es necesario analizar el HTML!
Robin Day
2
Si tiene una cadena arbitraria cuyo formato está completamente bajo su control, el hecho de que la cadena sea XML bien formada realmente no es relevante. Pero casi ningún caso de uso para XML en realidad cae en esta categoría.
Robert Rossney
15
Puedo decirle por experiencia dolorosa que la mayoría de las veces es posible obtener lo que desea utilizando patrones de expresiones regulares complejas absurdas. Hasta que el sitio web experimente un pequeño cambio hilarante y pueda arrojar esta expresión regular que lo hizo llorar por dos días por la ventana y comenzar de nuevo.
Thomasz
@Robert: "casi ningún caso de uso" es una exageración. En mi experiencia, hay casos de uso bastante comunes. YAGNI se aplica aquí ... a veces. El truco es saber cuán resistente y duradera debe ser su solución para la tarea particular que está abordando. Robin tiene un buen punto. Solo dice que el análisis XML completo no siempre vale la pena ... lo cual es cierto incluso si sabes cómo usarlo.
LarsH
6

Las personas normalmente escriben patrones codiciosos, a menudo lo suficiente como para que no se piense bien. * Arrastrando grandes trozos de archivo al <foo>. * </foo> más grande posible.

caos
fuente
2
Además de hacer que la repetición sea perezosa .*?<, puedes arreglar eso usando una clase de caracteres negada como [^<]*<. (Descargo de responsabilidad: obviamente, eso todavía no es infalible, que es el punto de la pregunta.)
Rory O'Kane
6

Estoy tentado a decir "no reinventes la rueda". Excepto que XML es un formato realmente complejo. Entonces quizás debería decir "no reinventes el sincrotrón".

Quizás el cliché correcto comienza "cuando todo lo que tienes es un martillo ..." Sabes cómo usar expresiones regulares, las expresiones regulares son buenas para analizar, así que ¿por qué molestarse en aprender una biblioteca de análisis XML?

Porque analizar XML es difícil . Cualquier esfuerzo que ahorre al no tener que aprender a usar una biblioteca de análisis XML estará más que compensado por la cantidad de trabajo creativo y la eliminación de errores que tendrá que hacer. Por su propio bien, busque en Google "biblioteca XML" y aproveche el trabajo de otra persona.

Isaac Rabinovitch
fuente
3
Sin embargo, no es tan complejo como C ++.
Cole Johnson
66
@Cole "Cole9" Johnson Yo tampoco usaría REs para analizar C ++.
Isaac Rabinovitch
2
Si XML es un sincrotrón, C ++ sería el Gran Colisionador de Hadrones.
Kevin Kostlan
4

Creo que este clásico tiene la información que estás buscando. Puede encontrar el punto en uno de los comentarios allí:

Creo que la falla aquí es que HTML es una gramática Chomsky Tipo 2 (gramática libre de contexto) y RegEx es una gramática Chomsky Tipo 3 (expresión regular). Dado que una gramática tipo 2 es fundamentalmente más compleja que una gramática tipo 3, no puede esperar que esto funcione . Pero muchos lo intentarán, algunos reclamarán el éxito y otros encontrarán la falla y lo arruinarán totalmente.

Alguna información más de Wikipedia: Jerarquía Chomsky

Adam Arold
fuente
66
"Expresión regular" no tiene exactamente el mismo significado en las discusiones formales de gramática que aquí. La mayoría de los motores regex existentes son más potentes que las gramáticas Chomsky Tipo 3 (por ejemplo, coincidencias no codiciosas, refuerzos). Algunos motores regex (como los de Perl) están completos en Turing. Es cierto que incluso esas son herramientas deficientes para analizar HTML, pero este argumento no es la razón.
dubiousjim
4

Creo que los problemas se reducen a:

  1. La expresión regular es casi siempre incorrecta. Hay entradas legítimas que no coincidirán correctamente. Si trabajas lo suficiente, puedes hacer que sea 99% correcto, o 99.999%, pero hacerlo 100% correcto es casi imposible, aunque solo sea por las cosas extrañas que XML permite al usar entidades.

  2. Si la expresión regular es incorrecta, incluso para el 0.00001% de las entradas, entonces tiene un problema de seguridad, porque alguien puede descubrir la única entrada que romperá su aplicación.

  3. Si la expresión regular es lo suficientemente correcta como para cubrir el 99,99% de los casos, será completamente ilegible e imposible de mantener.

  4. Es muy probable que una expresión regular funcione muy mal en archivos de entrada de tamaño moderado. Mi primer encuentro con XML fue reemplazar un script de Perl que (incorrectamente) analizaba los documentos XML entrantes con un analizador XML adecuado, y no solo reemplazamos 300 líneas de código ilegible con 100 líneas que cualquiera podría entender, sino que mejoramos el tiempo de respuesta del usuario de 10 segundos a aproximadamente 0.1 segundos.

Michael Kay
fuente
1

En términos generales, XML no se puede analizar utilizando expresiones regulares, ya que la gramática XML no es regular. En pocas palabras, las expresiones regulares no pueden contar (bueno, las expresiones regulares de Perl en realidad podrían contar cosas), por lo que no puede equilibrar las etiquetas de apertura-cierre.

Estoy en desacuerdo. Si va a utilizar recursivo en expresiones regulares, puede encontrar fácilmente las etiquetas de apertura y cierre.

Aquí mostré ejemplos de expresiones regulares para evitar errores de análisis de ejemplos en el primer mensaje.

Maxim Suslov
fuente
Primero, las expresiones regulares recursivas no son expresiones regulares (si miras entre paréntesis, verás que reconozco que las expresiones regulares de Perl, que son recursivas, pueden contar cosas, lo que se requiere para manejar HTML). En segundo lugar, su ejemplo es para XHTML o XML que está bien formado. HTML no está bien formado. En tercer lugar, debe preguntarse si es más fácil extender y mantener un analizador escrito en un lenguaje de expresiones regulares recursivo o en un lenguaje de programación de propósito general.
Chas. Owens
Cuarto, incluso su ejemplo se rompe trivialmente mientras sigue siendo XML válido. Agregue un espacio entre content_block e id y falla. Estoy seguro de que si pasara unos minutos más, encontraría algún otro error estructural en su código. Simplemente no es una buena idea.
Chas. Owens
1

Di una respuesta simplificada a este problema aquí . Si bien no representa la marca del 100%, explico cómo es posible si está dispuesto a hacer un trabajo de preprocesamiento.

Erutan409
fuente