Сведения о вопросе

Htmlщик

18:57, 1st August, 2020

Теги

html   regex   perl    

Regex, чтобы соответствовать всем тегам HTML, кроме

и

Просмотров: 517   Ответов: 13

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

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

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

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



  Сведения об ответе

SKY

12:21, 9th August, 2020

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

# 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 ing, как в приведенном выше) не так уж сложно. Результат будет гораздо более надежным, ремонтопригодным и, возможно, также более быстрым (HTML::Parser использует серверную часть на основе C), чем попытка использовать регулярные выражения.


  Сведения об ответе

P_S_S

21:06, 1st October, 2020

На мой взгляд, попытка разобрать 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, с которым вам придется иметь дело. Вы могли бы, конечно, придумать regex для его разбора, но, как уже предлагали другие, использовать реальный парсер HTML просто ооочень проще.


  Сведения об ответе

lats

12:28, 11th August, 2020

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

<(?!\/?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, но будет соответствовать заранее и похожие теги, с атрибутами или без.

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


  Сведения об ответе

#hash

02:00, 9th August, 2020

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

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

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

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


  Сведения об ответе

#hash

01:32, 20th August, 2020

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

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

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

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


  Сведения об ответе

SKY

07:04, 27th August, 2020

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

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

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


  Сведения об ответе

ASSembler

11:39, 7th August, 2020

Так как HTML не является обычным языком

HTML нет, но HTML теги есть, и они могут быть адекватно описаны регулярными выражениями.


  Сведения об ответе

crush

14:42, 21st August, 2020

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

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

EDIT:

Но это не будет соответствовать тегу <pre> или <param> , к сожалению.

Может быть, вот это?

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

Это должно охватывать <p> теги, которые также имеют атрибуты.


  Сведения об ответе

VERSUION

02:02, 22nd August, 2020

Вы также можете разрешить whitespace перед "p" в теге p. Не знаю, как часто вы будете сталкиваться с этим, но < p> вполне допустимо HTML.


  Сведения об ответе

padenie

06:45, 22nd August, 2020

Оригинал regex можно заставить работать с очень небольшим усилием:

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

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

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


  Сведения об ответе

screen

21:42, 4th August, 2020

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

Со всеми отказами от использования regex для разбора 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

Как соответствовать шаблону разве что...


  Сведения об ответе

repe

23:25, 26th August, 2020

Попробуйте это, должно сработать:

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

Пояснение: он соответствует либо одной букве, кроме “p”, за которой следует необязательный whitespace и более символов, либо нескольким буквам (по крайней мере, двум).

/EDIT: я добавил возможность обрабатывать атрибуты в тегах p .


  Сведения об ответе

prince

12:19, 1st August, 2020

Вероятно, вам также следует удалить все атрибуты на теге <p>, так как кто-то плохой может сделать что-то вроде:

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

Самый простой способ сделать это-использовать предложенный здесь regex человек для поиска &ltp> тегов с атрибутами и заменить их <p> тегами без атрибутов. Просто на всякий случай.


Ответить на вопрос

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