2008-08-27 18 views
19

Necesito hacer coincidir y eliminar todas las etiquetas con una expresión regular en Perl. Tengo el siguiente:Regex para que coincida con todas las etiquetas HTML, excepto <p>y</p>

<\\??(?!p).+?> 

Pero esto sigue coincidiendo con el </p> etiqueta de cierre. ¿Alguna pista sobre cómo hacer coincidir con la etiqueta de cierre también?

Nota, esto se está realizando en xhtml.

+3

Consulte esta respuesta para sus pensamientos/HTML Regex - http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454 # 1 732454 –

Respuesta

9

me ocurrió esto:

<(?!\/?p(?=>|\s.*>))\/?.*?> 

x/ 
<   # Match open angle bracket 
(?!   # Negative lookahead (Not matching and not consuming) 
    \/?  # 0 or 1/
    p   # p 
    (?=  # Positive lookahead (Matching and not consuming) 
    >  # > - No attributes 
     |  # or 
    \s  # whitespace 
    .*  # anything up to 
    >  # close angle brackets - with attributes 
    )   # close positive lookahead 
)   # close negative lookahead 
      # if we have got this far then we don't match 
      # a p tag or closing p tag 
      # with or without attributes 
\/?   # optional close tag symbol (/) 
.*?   # and anything up to 
>   # first closing tag 
/

Esto ahora se ocupará de etiquetas p con o sin atributos y las etiquetas p de cierre, pero coincidirá con etiquetas pre y similares, con o sin atributos.

No quita atributos, pero mis datos de origen no los ponen. Puedo cambiar esto más tarde para hacer esto, pero esto será suficiente por ahora.

+0

Finalmente, alguien que publique una respuesta sobre expresiones regulares y una explicación de cada parte. Usted señor, merece un premio intergaláctico! – SpaceDog

1

Suponiendo que esto funcionará en Perl como lo hace en los idiomas que pretenden utilizar la sintaxis compatible con Perl:

/<\/?[^p][^>]*>/

EDIT:

Pero eso no coincidirá con un <pre> o <param> etiqueta, desafortunadamente

Esto, ¿quizás?

/<\/?(?!p>|p)[^>]+>/ 

Eso debería cubrir <p> etiquetas que tienen atributos, también.

0

Prueba de esto, que debería funcionar:

/<\/?([^p](\s.+?)?|..+?)>/ 

Explicación: se ajusta con una sola letra, excepto una “p”, seguido de un espacio en blanco opcional y más caracteres o letras múltiples (al menos dos).

/EDITAR: He agregado la capacidad de manejar atributos en etiquetas p.

2

Dado que el HTML no es un lenguaje normal, no esperaría que una expresión regular hiciera un muy buen trabajo al hacerla coincidir. Podrían estar a la altura de esta tarea (aunque no estoy convencido), pero consideraría buscar en otro lado; Estoy seguro de que Perl debe tener algunas bibliotecas listas para usar para manipular HTML.

De todos modos, yo creo que lo que desea hacer coincidir es < /?(p.+|.*)(\s*.*) > no con avidez (no sé los caprichos de la sintaxis de expresiones regulares de Perl así que no puedo ayudar más). Estoy asumiendo que eso significa espacio en blanco. Tal vez no. De cualquier manera, desea algo que combine los atributos con el espacio en blanco del nombre de la etiqueta. Pero es más difícil que eso, ya que las personas a menudo colocan corchetes angulares sin escotaduras dentro de los guiones y comentarios, y tal vez incluso los valores de los atributos citados, con los que no desea coincidir.

Por lo tanto, como digo, realmente no creo que las expresiones regulares sean la herramienta adecuada para el trabajo.

2

Desde HTML no es un lenguaje regular

HTML no es más que etiquetas HTML son y pueden ser descritos por adequatly expresiones regulares.

-1

Debería probablemente eliminar cualquier atributo de la etiqueta < p>, ya que alguien malo podría hacer algo como:

<p onclick="document.location.href='http://www.evil.com'">Clickable text</p> 

La forma más sencilla de hacer esto, es el uso de la gente de expresiones regulares sugieren aquí para buscar para & ltp> etiquetas con atributos, y reemplácelas con < p> etiquetas sin atributos. Sólo para estar en el lado seguro.

3

No estoy seguro de por qué quieres hacer esto - regex para la desinfección de HTML no siempre es el mejor método (debes recordar desinfectar los atributos y demás, eliminar javascript: hrefs y los que te gustan) ... pero, una expresiones regulares para que coincida con las etiquetas HTML que no son <p></p>:

(<[^pP].*?>|</[^pP]>)

detallado:

(
    <    # < opening tag 
     [^pP].*? # p non-p character, then non-greedy anything 
    >    # > closing tag 
|     # ....or.... 
    </    # </ 
     [^pP]  # a non-p tag 
    >    # > 
) 
37

Si insiste en el uso de una expresión regular, algo así como º se trabajará en la mayoría de los casos:

# Remove all HTML except "p" tags 
$html =~ s{<(?>/?)(?:[^pP]|[pP][^\s>/])[^>]*>}{}g; 

Explicación:

s{ 
    <    # opening angled bracket 
    (?>/?)  # ratchet past optional/
    (?: 
    [^pP]  # non-p tag 
    |   # ...or... 
    [pP][^\s>/] # longer tag that begins with p (e.g., <pre>) 
) 
    [^>]*   # everything until closing angled bracket 
    >    # closing angled bracket 
}{}gx; # replace with nothing, globally 

Pero, en realidad, se ahorrará algunos dolores de cabeza y utilizar un analizador en su lugar. CPAN tiene varios módulos que son adecuados. Aquí hay un ejemplo usando el módulo HTML::TokeParser que viene con la distribución extremadamente capaz HTML::Parser CPAN:

use strict; 

use HTML::TokeParser; 

my $parser = HTML::TokeParser->new('/some/file.html') 
    or die "Could not open /some/file.html - $!"; 

while(my $t = $parser->get_token) 
{ 
    # Skip start or end tags that are not "p" tags 
    next if(($t->[0] eq 'S' || $t->[0] eq 'E') && lc $t->[1] ne 'p'); 

    # Print everything else normally (see HTML::TokeParser docs for explanation) 
    if($t->[0] eq 'T') 
    { 
    print $t->[1]; 
    } 
    else 
    { 
    print $t->[-1]; 
    } 
} 

HTML::Parser acepta de entrada en la forma de un nombre de archivo, un identificador de archivo abierto, o una cadena. Envolver el código anterior en una biblioteca y hacer que el destino sea configurable (es decir, no solo print como en el ejemplo anterior) no es difícil. El resultado será mucho más confiable, mantenible y posiblemente también más rápido (HTML :: Parser usa un backend basado en C) que tratar de usar expresiones regulares.

+0

Ahórrese más dolor de cabeza y use el excelente módulo HTML :: TokeParser :: Simple. :-) –

1

También es posible que desee dejar espacio en blanco antes de la "p" en la etiqueta p. No estoy seguro de la frecuencia con la que se encontrará con esto, pero < p> es un HTML perfectamente válido.

16

En mi opinión, intentar analizar HTML con algo que no sea un analizador HTML es solo pedir un mundo de dolor. HTML es un realmente idioma complejo (que es una de las principales razones por las que se creó XHTML, que es mucho más simple que HTML).

Por ejemplo, esto:

<HTML/
    <HEAD/
    <TITLE/>/
    <P/> 

es un 100% bien formada, 100% documento HTML completa, válida.(Bueno, se está perdiendo la declaración DOCTYPE, pero aparte de eso ...)

Es semánticamente equivalente a

<html> 
    <head> 
    <title> 
     &gt; 
    </title> 
    </head> 
    <body> 
    <p> 
     &gt; 
    </p> 
    </body> 
</html> 

Pero es, sin embargo, HTML válido que vas a tener que tratar. Usted podría, por supuesto, idear una expresión regular para analizarlo, pero, como otros ya lo han sugerido, usar un analizador de HTML real es mucho más fácil.

+1

Wow. No te creí, pero lo ejecuté con el validador W3 con un tipo de documento Strict HTML 4.01 estricto, y lo valida. Lanza advertencias, pero guau. – eyelidlessness

+0

örg, eres ** así que ** ¡correcto! Sin embargo, si los conjuntos de entrada están muy bien restringidos, no es tan malo. Con los aleatorios, sin embargo, sería una locura no usar una clase de análisis. Deje que alguien más haga todo el trabajo duro! – tchrist

1

La expresión regular original puede llegar a funcionar con muy poco esfuerzo:

<(?>/?)(?!p).+?> 

El problema era que la /? (o \?) abandonó lo que coincidía cuando la afirmación después de que falló. Al usar un grupo que no realiza rastreos (?> ...) a su alrededor, se cuida de que nunca suelte la barra coincidente, por lo que la afirmación (?! P) siempre está anclada al comienzo del texto de la etiqueta.

(Dicho esto, estoy de acuerdo en que generalmente analizar HTML con expresiones regulares no es el camino a seguir).

3

Utilicé Xetius regex y funciona bien. Excepto algunas etiquetas generadas por flexión que pueden ser:
sin espacios dentro. Intenté arreglarlo con un simple ? después \ s y parece que está funcionando:

<(?!\/?p(?=>|\s?.*>))\/?.*?> 

lo estoy usando para borrar las etiquetas de flex generó texto html, así que también agregó etiquetas más exceptuados:

<(?!\/?(p|a|b|i|u|br)(?=>|\s?.*>))\/?.*?> 
1

Xetius, resucitando esta antigua pregunta porque tenía una solución simple que no se mencionaba. (Encontró su pregunta mientras hacía una investigación para un regex bounty quest.)

Con todas las advertencias sobre el uso de expresiones regulares para analizar html, aquí hay una manera simple de hacerlo.

#!/usr/bin/perl 
$regex = '(<\/?p[^>]*>)|<[^>]*>'; 
$subject = 'Bad html <a> </I> <p>My paragraph</p> <i>Italics</i> <p class="blue">second</p>'; 
($replaced = $subject) =~ s/$regex/$1/eg; 
print $replaced . "\n"; 

Ver este live demo

Referencia

How to match pattern except in situations s1, s2, s3

How to match a pattern unless...

Cuestiones relacionadas