代码之家  ›  专栏  ›  技术社区  ›  Xetius

正则表达式匹配除<p>和<p>之外的所有HTML标签</p>

  •  24
  • Xetius  · 技术社区  · 17 年前

    我需要使用Perl中的正则表达式来匹配和删除所有标签。我有以下几点:

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

    但这仍然与闭幕式相匹配 </p> 标签。还有关于如何与结束标签匹配的提示吗?

    注意,这是在xhtml上执行的。

    14 回复  |  直到 15 年前
        1
  •  38
  •   John Siracusa    17 年前

    如果你 坚持 在使用正则表达式时,在大多数情况下,这样的方法都会奏效:

    # 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::分析器 接受文件名、打开的文件句柄或字符串形式的输入。将上述代码封装在库中,并使目标可配置(即,不仅仅是 print 如上所述)并不难。结果将比尝试使用正则表达式更可靠、更可维护,而且可能更快(HTML::Parser使用基于C的后端)。

        2
  •  16
  •   Jörg W Mittag    17 年前

    在我看来,试图用HTML解析器以外的任何东西来解析HTML只是在自找麻烦。HTML是一个 真正地 复杂语言(这是创建XHTML的主要原因之一,XHTML比HTML简单得多)。

    例如:

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

    是一个完整、100%格式良好、100%有效的HTML文档。(好吧,它缺少DOCTYPE声明,但除此之外……)

    它在语义上等价于

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

    但它仍然是你必须处理的有效HTML。你 可能会 当然,设计一个正则表达式来解析它,但是,正如其他人已经建议的那样,使用实际的HTML解析器要容易得多。

        3
  •  14
  •   Xetius    17 年前

    我想到了这个:

    <(?!\/?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标签,但将匹配具有或没有属性的前标签和类似标签。

    它不会去掉属性,但我的源数据不会把它们放进去。我可能会在以后更改它,但现在这就足够了。

        4
  •  5
  •   y_nk    15 年前

    我使用了Xetius正则表达式,它工作得很好。除了一些flex生成的标签,它们可以是:
    里面没有空间。我试着用一个简单的 ? 之后 s 看起来它正在工作:

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

    我用它来清除flex生成的html文本中的标签,所以我还添加了更多例外标签:

    <(?!\/?(p|a|b|i|u|br)(?=>|\s?.*>))\/?.*?>
    
        5
  •  4
  •   dbr    17 年前

    不知道你为什么要这样做——用于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
        >               # >
    )
    
        6
  •  3
  •   Community CDub    8 年前

    Xetius重新提出了这个古老的问题,因为它有一个没有提到的简单解决方案。(在为某人做研究时发现了你的问题 regex bounty quest .)

    关于使用正则表达式解析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";
    

    看这个 live demo

    参考

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

    How to match a pattern unless...

        7
  •  2
  •   DrPizza    17 年前

    由于HTML不是一种正则语言,我不希望正则表达式在匹配它方面做得很好。他们可能能胜任这项任务(尽管我不相信),但我会考虑其他地方;我确信perl一定有一些现成的库来操纵HTML。

    不管怎样,我认为你想要匹配的是</?(p.+|.*)(\s*.*)>非贪婪地(我不知道perl正则表达式语法的变幻莫测,所以我无法进一步解释)。我假设\ s表示空白。也许事实并非如此。无论哪种方式,您都需要一些与标记名中空格偏移的属性相匹配的东西。但这比这更困难,因为人们经常在脚本和注释中放入无括号的尖括号,甚至可能引用属性值,而你不想与之匹配。

    所以正如我所说,我真的不认为regexps是完成这项工作的合适工具。

        8
  •  2
  •   Konrad Rudolph    17 年前

    因为HTML不是一种常规语言

    HTML不是,但HTML标签是,它们可以用正则表达式充分描述。

        9
  •  1
  •   sergtk    17 年前

    假设这将在PERL中工作,就像在声称使用PERL兼容语法的语言中一样:

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

    编辑:

    但这与a不匹配 <pre> <param> 不幸的是,标签。

    也许是这个?

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

    这应该包括 <p> 也有属性的标签。

        10
  •  1
  •   Kibbee    17 年前

    您可能还希望在p标签中的“p”之前允许空白。不确定多久会遇到这种情况,但是<p>;是完全有效的HTML。

        11
  •  1
  •   moritz    17 年前

    原始正则表达式可以轻松使用:

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

    问题是/?(或?)在断言失败后放弃了匹配的内容。在它周围使用非回溯组(?>…)可以确保它永远不会释放匹配的斜线,因此(?!p)断言始终锚定在标记文本的开头。

    (也就是说,我同意通常用正则表达式解析HTML不是正确的做法)。

        12
  •  0
  •   Konrad Rudolph    17 年前

    试试这个,它应该奏效:

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

    说明:它可以匹配除p之外的单个字母,后跟可选的空格和更多字符,也可以匹配多个字母(至少两个)。

    /EDIT:我在中添加了处理属性的能力 p 标签。

        13
  •  0
  •   Adebowale    4 年前

    这对我来说是可行的,因为上述所有解决方案对于以p开头的其他html标签都失败了,比如param-prepress等。它还处理html属性。

    ~(<\/?[^>]*(?<!<\/p|p)>)~ig
    
        14
  •  -1
  •   Vegard Larsen Julie Lerman    17 年前

    您可能还应该删除<p>;标签,因为坏人可以做这样的事情:

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

    最简单的方法是使用人们在这里建议的正则表达式来搜索&ltp>带有属性的标签,并将其替换为<p>;没有属性的标签。只是为了安全起见。