Pregunta

Necesito hacer coincidir y eliminar todas las etiquetas usando una expresión regular en Perl.Tengo lo siguiente:

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

Pero esto todavía coincide con el cierre. </p> etiqueta.¿Alguna pista sobre cómo combinar también con la etiqueta de cierre?

Tenga en cuenta que esto se realiza en xhtml.

¿Fue útil?

Solución 3

Se 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 las etiquetas p con o sin atributos y las etiquetas p de cierre, pero coincidirá con las etiquetas pre y similares, con o sin atributos.

No elimina los atributos, pero mis datos de origen no los incluyen.Puede que cambie esto más adelante para hacer esto, pero será suficiente por ahora.

Otros consejos

Si usted insistir sobre el uso de una expresión regular, algo como esto funcionará 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 realmente, ahórrese algunos dolores de cabeza y utilice un analizador en su lugar.CPAN tiene varios módulos que son adecuados.A continuación se muestra un ejemplo utilizando el HTML::TokeParser módulo que viene con el extremadamente capaz HTML::Analizador Distribución de 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::Analizador acepta entradas en forma de nombre de archivo, identificador de archivo abierto o cadena.Envolver el código anterior en una biblioteca y hacer que el destino sea configurable (es decir, no solo print(como en lo anterior) no es difícil.El resultado será mucho más confiable, fácil de mantener y posiblemente también más rápido (HTML::Parser usa un backend basado en C) que intentar usar expresiones regulares.

En mi opinión, intentar analizar HTML con cualquier otra cosa que no sea un analizador HTML es simplemente pedir un mundo de dolor.HTML es un en realidad lenguaje 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 documento HTML completo, 100% bien formado y 100% válido.(Bueno, falta 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, aun así, tendrás que lidiar con HTML válido.Tú podría, Por supuesto, diseñe una expresión regular para analizarlo, pero, como ya sugirieron otros, usar un analizador HTML real es muchísimo más fácil.

No estoy seguro de por qué quiere hacer esto: las expresiones regulares para la desinfección de HTML no siempre son el mejor método (debe recordar desinfectar los atributos y demás, eliminar javascript:hrefs y similares)...pero una expresión regular para que coincida con etiquetas HTML que no lo son <p></p>:

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

Verboso:

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

Utilicé Xetius regex y funciona bien.Excepto por algunas etiquetas generadas por flex que pueden ser:
sin espacios en el interior.Intenté solucionarlo con un simple ? después \s y parece que está funcionando:

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

Lo estoy usando para borrar etiquetas del texto html generado por flex, por lo que también agregué más etiquetas exceptuadas:

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

Dado que HTML no es un lenguaje regular, no esperaría que una expresión regular hiciera un buen trabajo al igualarlo.Es posible que estén a la altura de esta tarea (aunque no estoy convencido), pero consideraría buscar en otra parte;Estoy seguro de que Perl debe tener algunas bibliotecas disponibles para manipular HTML.

De todos modos, creo que lo que quieres hacer coincidir es </?(p.+|.*)(\s*.*)> sin avidez (no conozco los caprichos de la sintaxis de expresiones regulares de Perl, así que no puedo ayudar más).Supongo que \s significa espacios en blanco.Quizás no sea así.De cualquier manera, desea algo que coincida con los atributos desplazados del nombre de la etiqueta por espacios en blanco.Pero es más difícil que eso, ya que la gente a menudo coloca corchetes angulares sin escape dentro de guiones y comentarios y tal vez incluso cita valores de atributos, con los que no desea comparar.

Como digo, realmente no creo que las expresiones regulares sean la herramienta adecuada para el trabajo.

Dado que HTML no es un lenguaje normal

HTML no lo es, pero las etiquetas HTML sí lo son y pueden describirse adecuadamente mediante expresiones regulares.

Suponiendo que esto funcionará en PERL como lo hace en idiomas que afirman utilizar una sintaxis compatible con PERL:

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

EDITAR:

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

¿Esto, tal vez?

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

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

También es posible que desee permitir espacios en blanco antes de la "p" en la etiqueta p.No estoy seguro de con qué frecuencia te encontrarás con esto, pero <p> es HTML perfectamente válido.

Se puede hacer que la expresión regular original funcione con muy poco esfuerzo:

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

El problema era que el /?(o \?) renunció a lo que coincidía cuando la afirmación posterior falló.El uso de un grupo sin retroceso (?>...) a su alrededor tiene cuidado de que nunca libere la barra diagonal coincidente, por lo que la aserción (?!p) siempre está anclada al inicio del texto de la etiqueta.

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

Xetius, resucitando esta antigua pregunta porque tenía una solución sencilla que no se mencionaba.(Encontré su pregunta mientras investigaba un poco para un búsqueda de recompensas de expresiones regulares.)

Con todas las exenciones de responsabilidad sobre el uso de expresiones regulares para analizar HTML, aquí hay una forma sencilla 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";

Mira esto demo en vivo

Referencia

Cómo hacer coincidir el patrón excepto en las situaciones s1, s2, s3

Cómo combinar un patrón a menos que...

Prueba esto, debería funcionar:

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

Explicación:coincide con una sola letra excepto "p", seguida de un espacio en blanco opcional y más caracteres, o varias letras (al menos dos).

/EDITAR:Agregué la capacidad de manejar atributos en p etiquetas.

Probablemente también deberías 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 utilizar las expresiones regulares que la gente sugiere aquí para buscar etiquetas &ltp> con atributos y reemplazarlas con etiquetas <p> sin atributos.Sólo para estar en el lado seguro.

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