Pergunta

Preciso combinar e remover todas as tags usando uma expressão regular em Perl.Eu tenho o seguinte:

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

Mas isso ainda coincide com o fechamento </p> marcação.Alguma dica sobre como combinar com a tag de fechamento também?

Observe que isso está sendo executado em xhtml.

Foi útil?

Solução 3

Eu descobri isso:

<(?!\/?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
/

Isso agora lidará com tags p com ou sem atributos e as tags p de fechamento, mas corresponderá a tags pré e semelhantes, com ou sem atributos.

Ele não remove atributos, mas meus dados de origem não os colocam.Posso mudar isso mais tarde para fazer isso, mas isso será suficiente por enquanto.

Outras dicas

Se você insistir ao usar um regex, algo assim funcionará na maioria dos casos:

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

Explicação:

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

Mas, na verdade, evite algumas dores de cabeça e use um analisador.O CPAN possui vários módulos adequados.Aqui está um exemplo usando o HTML::TokeParser módulo que vem com o extremamente capaz HTML::Analisador Distribuição 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::Analisador aceita entrada na forma de um nome de arquivo, um identificador de arquivo aberto ou uma string.Envolver o código acima em uma biblioteca e tornar o destino configurável (ou seja, não apenas printcomo acima) não é difícil.O resultado será muito mais confiável, sustentável e possivelmente também mais rápido (HTML::Parser usa um backend baseado em C) do que tentar usar expressões regulares.

Na minha opinião, tentar analisar HTML com qualquer coisa que não seja um analisador de HTML é apenas pedir muito sofrimento.HTML é um realmente linguagem complexa (que é uma das principais razões pela qual o XHTML foi criado, que é muito mais simples que o HTML).

Por exemplo, isto:

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

é um documento HTML completo, 100% bem formado e 100% válido.(Bem, está faltando a declaração DOCTYPE, mas fora isso...)

É semanticamente equivalente a

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

Mesmo assim, é um HTML válido com o qual você terá que lidar.Você poderia, é claro, crie um regex para analisá-lo, mas, como outros já sugeriram, usar um analisador HTML real é muito mais fácil.

Não sei por que você deseja fazer isso - regex para higienização de HTML nem sempre é o melhor método (você precisa se lembrar de higienizar atributos e coisas assim, remova o javascript:hrefs e similares)...mas, um regex para corresponder às tags HTML que não são <p></p>:

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

Verboso:

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

Eu usei o regex Xetius e funciona bem.Exceto algumas tags geradas por flex que podem ser:
sem espaços dentro.Eu tentei consertar isso com um simples ? depois \s e parece que está funcionando:

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

Estou usando-o para limpar tags de texto HTML gerado por flex, então também adicionei mais tags excluídas:

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

Como o HTML não é uma linguagem regular, não esperaria que uma expressão regular fizesse um bom trabalho de correspondência.Eles podem estar à altura dessa tarefa (embora eu não esteja convencido), mas eu consideraria procurar outro lugar;Tenho certeza que o Perl deve ter algumas bibliotecas prontas para manipular HTML.

De qualquer forma, eu acho que o que você deseja corresponder é </?(p.+|.*)(\s*.*)> não avidamente (não conheço os caprichos da sintaxe regexp do perl, então não posso ajudar avançar).Estou assumindo que \s significa espaço em branco.Talvez não.De qualquer forma, você deseja algo que corresponda aos atributos deslocados do nome da tag por espaço em branco.Mas é mais difícil do que isso, pois as pessoas geralmente colocam colchetes sem escape dentro de scripts e comentários e talvez até valores de atributos entre aspas, com os quais você não deseja comparar.

Então, como eu disse, não acho que as expressões regulares sejam a ferramenta certa para o trabalho.

Como HTML não é uma linguagem regular

HTML não é, mas tags HTML são e podem ser adequadamente descritas por expressões regulares.

Supondo que isso funcionará em PERL assim como em linguagens que afirmam usar sintaxe compatível com PERL:

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

EDITAR:

Mas isso não corresponderá a um <pre> ou <param> etiqueta, infelizmente.

Isso, talvez?

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

Isso deve cobrir <p> tags que também possuem atributos.

Você também pode permitir espaços em branco antes do "p" na tag p.Não tenho certeza com que frequência você encontrará isso, mas < p> é HTML perfeitamente válido.

O regex original pode funcionar com muito pouco esforço:

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

O problema era que o /?(ou \?) desistiu do que correspondia quando a afirmação posterior falhou.Usar um grupo sem retrocesso (?>...) em torno dele cuida para que ele nunca libere a barra correspondente, de modo que a afirmação (?!p) esteja sempre ancorada no início do texto da tag.

(Dito isto, concordo que geralmente analisar HTML com expressões regulares não é o caminho a percorrer).

Xécio, ressuscitando esta questão antiga porque tinha uma solução simples que não foi mencionada.(Encontrei sua pergunta enquanto fazia uma pesquisa para um missão de recompensa regex.)

Com todas as isenções de responsabilidade sobre o uso de regex para analisar HTML, aqui está uma maneira simples de fazer isso.

#!/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";

Veja isso demonstração ao vivo

Referência

Como combinar o padrão exceto nas situações s1, s2, s3

Como combinar um padrão, a menos que...

Tente isso, deve funcionar:

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

Explicação:corresponde a uma única letra, exceto “p”, seguida por um espaço em branco opcional e mais caracteres, ou a várias letras (pelo menos duas).

/EDITAR:Eu adicionei a capacidade de lidar com atributos em p Tag.

Você provavelmente também deve remover quaisquer atributos da tag <p>, já que alguém ruim poderia fazer algo como:

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

A maneira mais fácil de fazer isso é usar o regex sugerido aqui para pesquisar tags &ltp> com atributos e substituí-las por tags <p> sem atributos.Só para ficar no lado seguro.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top