Regex для соответствия всем тэгам HTML кроме <p>и </p> - PullRequest
21 голосов
/ 27 августа 2008

Мне нужно сопоставить и удалить все теги с помощью регулярного выражения в Perl. У меня есть следующее:

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

Но это по-прежнему совпадает с закрывающим тегом </p>. Любой совет о том, как сопоставить с закрывающим тегом?

Обратите внимание, это выполняется в xhtml.

Ответы [ 13 ]

37 голосов
/ 27 августа 2008

Если вы настаиваете на использовании регулярных выражений, то в большинстве случаев что-то подобное будет работать:

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

Пояснение:

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

Но на самом деле, избавьте себя от головной боли и используйте вместо этого парсер. CPAN имеет несколько подходящих модулей. Вот пример использования модуля HTML :: TokeParser , который поставляется с чрезвычайно способным 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 принимает входные данные в форме имени файла, дескриптора открытого файла или строки. Заключение вышеуказанного кода в библиотеку и настройка места назначения (т. Е. Не просто print, как в приведенном выше) не является сложной задачей. Результат будет намного более надежным, поддерживаемым и, возможно, также более быстрым (HTML :: Parser использует бэкэнд на основе C), чем попытка использовать регулярные выражения.

16 голосов
/ 27 августа 2008

По моему мнению, попытка парсинга HTML с чем-то, кроме парсера HTML, просто требует мира боли. HTML является действительно сложным языком (что является одной из основных причин создания XHTML, что гораздо проще, чем HTML).

Например, это:

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

является полным, на 100% правильно сформированным, на 100% действительным HTML-документом. (Ну, в нем отсутствует объявление DOCTYPE, но кроме этого ...)

Это семантически эквивалентно

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

Но, тем не менее, это правильный HTML, с которым вам придется иметь дело. Вы, конечно, могли бы разработать регулярное выражение для его синтаксического анализа, но, как уже предлагали другие, использовать настоящий анализатор HTML просто ооочень просто.

10 голосов
/ 27 августа 2008

Я придумал это:

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

Теперь будет работать с p-тегами с атрибутами или без атрибутов и с закрывающими p-тегами, но будут совпадать с тегами pre и аналогичными, с атрибутами или без них.

Он не удаляет атрибуты, но мои исходные данные не помещают их. Я могу изменить это позже, чтобы сделать это, но пока этого будет достаточно.

3 голосов
/ 28 мая 2010

Я использовал Xetius Regex, и он отлично работает. За исключением некоторых сгенерированных тегов, которые могут быть:
без пробелов внутри. Я попытался исправить это с помощью простого ? после \ s , и похоже, что оно работает:

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

Я использую его для очистки тегов от сгенерированного Flex HTML-текста, поэтому я также добавил больше исключенных тегов:

<(?!\/?(p|a|b|i|u|br)(?=>|\s?.*>))\/?.*?>
3 голосов
/ 27 августа 2008

Не уверен, почему вы хотите это сделать - регулярное выражение для очистки HTML не всегда лучший метод (вам нужно помнить, чтобы очистить атрибуты и тому подобное, удалите javascript: hrefs и подобные) ... но, регулярное выражение чтобы соответствовать тегам HTML, которые не <p></p>:

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

Многословный:

(
    <               # < opening tag
        [^pP].*?    # p non-p character, then non-greedy anything
    >               # > closing tag
|                   #   ....or....
    </              # </
        [^pP]       # a non-p tag
    >               # >
)
2 голосов
/ 27 августа 2008

Поскольку HTML не является обычным языком

HTML - это не HTML-теги, и их можно адекватно описать с помощью регулярных выражений.

2 голосов
/ 27 августа 2008

Поскольку HTML не является регулярным языком, я не ожидал бы, что регулярное выражение очень хорошо справится с сопоставлением. Возможно, они справятся с этой задачей (хотя я не уверен), но я бы подумал поискать в другом месте; Я уверен, что Perl должен иметь несколько готовых библиотек для манипулирования HTML.

В любом случае, я думаю, что вы хотите сопоставить </? (P. + |. *) (\ S *. *)> Не жадно (я не знаю капризов синтаксиса регулярных выражений perl, так Я не могу помочь дальше). Я предполагаю, что \ s означает пробел. Возможно, это не так. В любом случае вам нужно что-то, что будет соответствовать атрибутам, смещенным от имени тега на пробел. Но это сложнее, чем когда люди часто помещают неэкранированные угловые скобки внутри сценариев и комментариев и, возможно, даже указывают значения атрибутов в кавычках, с которыми вы не хотите сравнивать.

Итак, как я уже сказал, я не думаю, что регулярные выражения являются подходящим инструментом для работы.

1 голос
/ 14 мая 2014

Xetius, воскрешая этот древний вопрос, потому что у него было простое решение, которое не было упомянуто. (Нашел свой вопрос во время исследования квеста regex bounty .)

Со всеми оговорками об использовании регулярных выражений для разбора html, вот простой способ сделать это.

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

См. живое демо

Ссылка

Как сопоставить шаблон, кроме случаев s1, s2, s3

Как сопоставить шаблон, если ...

1 голос
/ 19 сентября 2008

Оригинальное регулярное выражение можно заставить работать без особых усилий:

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

Проблема была в том, что /? (или \?) отказался от того, что соответствовало ему, когда утверждение после его провала. Используя группу без возврата (?> ...) вокруг, он заботится о том, чтобы она никогда не освобождала совпадающую косую черту, поэтому утверждение (?! P) всегда привязано к началу текста тега.

(Тем не менее я согласен, что в общем случае разбор HTML с помощью регулярных выражений - это не тот путь).

1 голос
/ 27 августа 2008

Вы также можете захотеть использовать пробел перед «p» в теге p. Не знаю, как часто вы будете сталкиваться с этим, но

- это совершенно правильный HTML.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...