【问题标题】:Regex - Matching Tag Names Only in HTML正则表达式 - 仅在 HTML 中匹配标记名称
【发布时间】:2011-11-03 03:27:18
【问题描述】:

如何使用正则表达式检索 html sn-p 中的所有 html 标签名称?如果重要的话,我正在使用 PHP 来执行此操作。例如:

<div id="someid">
     <img src="someurl" />
     <br />
     <p>some content</p>
</div>

应该返回:div、img、br、p。

【问题讨论】:

    标签: html regex


    【解决方案1】:

    这应该适用于大多数格式良好的标记,前提是您不在 CDATA 部分并且没有玩过重新定义实体的讨厌游戏:

    # nasty, ugly, illegible, unmaintable — NEVER USE THIS STYLE!!!!
    /<\w+(?:\s+\w+=(?:\S+|(['"])(?:(?!\1).)*?\1))*\s*\/?>/s
    

    或者更清晰的,如

    # broken out into related elements grouped by whitespace via /x
    / < \w+ (?: \s+ \w+ = (?: \S+ | (['"]) (?: (?! \1) . ) *? \1 )) * \s* \/? > /xs
    

    甚至更清晰:

    / 
       # start of tag, with named ident
       < \w+ 
       # now with unlimited k=v pairs 
       #    where k is \w+ 
       #      and v is either \S+ or else quoted 
       (?: \s+ \w+ = (?: \S+        # either an unquoted value, 
                       | ( ['"] )   # or else first pick either quote
                         (?: 
                            (?! \1) .  # anything that isn't our quote, including brackets
                         ) * ?     # maximal should probably work here
                         \1        # till we see it again
                     ) 
       )  *    # as many k=v pairs as we can find
       \s *    # tolerate closing whitespace
    
       \/ ?    # XHTML style close tag
       >       # finally done
    /xs
    

    您可以在此处添加一些草率,例如在我没有超出的几个地方容忍空白。

    PHP 不一定是此类工作的最佳语言,尽管您可以在紧要关头凑合。最起码,您应该将这些东西隐藏在某个函数和/或变量中,而不是让它像裸露的一样暴露在外,考虑到 The Children Are Watching™。

    要做任何比找到字母或空格更复杂的事情,模式从 cmets 和空格中受益匪浅。这是不言而喻的,但由于某种原因,人们忘记使用/x 进行认知分块,让空白对相关的事物进行分组,就像使用命令式代码一样。

    即使它们是声明性程序而不是命令性程序,模式也可以从完整的问题分解和自上而下的设计中受益。实现这一点的一种方法是拥有“正则表达式子例程”您与使用它们的地方分开声明。否则你只是在做剪切和粘贴代码重用,这是悲观排序的代码重用。这是一个匹配&lt;img&gt; 标签的示例模式,这次使用的是真正的 Perl:

    my $img_rx = qr{
    
        # save capture in $+{TAG} variable
        (?<TAG> (?&image_tag) )
    
        # remainder is pure declaration
        (?(DEFINE)
    
            (?<image_tag>
                (?&start_tag)
                (?&might_white) 
                (?&attributes) 
                (?&might_white) 
                (?&end_tag)
            )
    
            (?<attributes>
                (?: 
                    (?&might_white) 
                    (?&one_attribute) 
                ) *
            )
    
            (?<one_attribute>
                \b
                (?&legal_attribute)
                (?&might_white) = (?&might_white) 
                (?:
                    (?&quoted_value)
                  | (?&unquoted_value)
                )
            )
    
            (?<legal_attribute> 
                (?: (?&required_attribute)
                  | (?&optional_attribute)
                  | (?&standard_attribute)
                  | (?&event_attribute)
                  # for LEGAL parse only, comment out next line 
                  | (?&illegal_attribute)
                )
            )
    
            (?<illegal_attribute> \b \w+ \b )
    
            (?<required_attribute>
                alt
              | src
            )
    
            (?<optional_attribute>
                (?&permitted_attribute)
              | (?&deprecated_attribute)
            )
    
            # NB: The white space in string literals 
            #     below DOES NOT COUNT!   It's just 
            #     there for legibility.
    
            (?<permitted_attribute>
                height
              | is map
              | long desc
              | use map
              | width
            )
    
            (?<deprecated_attribute>
                 align
               | border
               | hspace
               | vspace
            )
    
            (?<standard_attribute>
                class
              | dir
              | id
              | style
              | title
              | xml:lang
            )
    
            (?<event_attribute>
                on abort
              | on click
              | on dbl click
              | on mouse down
              | on mouse out
              | on key down
              | on key press
              | on key up
            )
    
            (?<unquoted_value> 
                (?&unwhite_chunk) 
            )
    
            (?<quoted_value>
                (?<quote>   ["']      )
                (?: (?! \k<quote> ) . ) *
                \k<quote> 
            )
    
            (?<unwhite_chunk>   
                (?:
                    # (?! [<>'"] ) 
                    (?! > ) 
                    \S
                ) +   
            )
    
            (?<might_white>     \s *   )
    
            (?<start_tag>  
                < (?&might_white) 
                img 
                \b       
            )
    
            (?<end_tag>          
                (?&html_end_tag)
              | (?&xhtml_end_tag)
            )
    
            (?<html_end_tag>       >  )
            (?<xhtml_end_tag>    / >  )
    
        )
    
    }six;
    

    是的,它会变长,但随着变长,它变得更易于维护,而不是更少。 它也更正确。 现在,使用它的实际程序不仅仅如此,因为您必须考虑比实际 HTML 更多的内容,例如 CDATA 和编码和顽皮的实体重新定义。然而,与流行的看法相反,你可以实际上用 PHP 做那种事情,因为它使用 PCRE,它允许 (?(DEFINE)...) 块和递归模式。在我的回答hereherehereherehere 中有更严肃的这类事情的例子。

    好的,很好,您是否阅读了所有这些内容,或者至少看了一眼?还在我这儿?你好??不要忘记呼吸。在那里,你现在会没事的。 :)

    当然,有一个很大的灰色区域,在那里,可能的事情让位于不明智的事情,而且比它屈服于不可能的事情要快得多。如果这些答案中的这些示例,更不用说当前答案中的这些示例,超出了您自己当前的模式匹配技能水平,那么您可能应该使用其他东西,这通常意味着让其他人为您做这件事。

    【讨论】:

    • 之前没有遇到过(?(DEFINE)...)。你知道它是否只是 Perl 和 PCRE,还是有其他支持它的实现? (我没有从 Google 获得任何有用的信息。)
    • @Peter:是的,这些是谷歌无法搜索到的东西,因为非字母数字被丢弃并忽略了大小写。当它发生时我没有注意,但我的直觉是 Perl 从 PCRE 得到它,而不是相反。我不知道还有什么支持它。它有很多问题——如果你看看我的程序,由于缺乏命名空间控制/巡逻,我不得不为常见的子例程定义复制东西——但它仍然非常酷。
    【解决方案2】:

    正则表达式可能并不总是有效。如果您 100% 确定它是格式良好的 XHTML,那么正则表达式可能是一种方法。如果没有,请使用某种 PHP 库来执行此操作。在 C# 中,有一个叫做 HTML Agility Pack 的东西,http://htmlagilitypack.codeplex.com,例如见How do I parse HTML using regular expressions in C#?。也许PHP中有一个等效的工具。

    【讨论】:

    • 我在vi 中一直使用/color="#000000":g/&lt;(a|href)/p 之类的东西:你有什么问题吗?我当然希望没有!如果你对此没有问题,那么你不应该告诉人们他们不能在 HTML 上使用模式匹配,因为这不仅明显不真实,而且你说一套做一套也是虚伪的。
    • tchrist,事实是,如果您的 XHTML 格式良好,那么您上面提到的一次性搜索可能不是问题。但是为了更健壮的解决方案,我决定使用 php 的 domdocument 类作为解析器。
    【解决方案3】:

    我想这应该可行...我会在一分钟内尝试:

    编辑: 删除 \s+(感谢 Peteris)

    preg_match_all('/<(\w+)[^>]*>/', $html, $matched_elements);
    

    【讨论】:

    • 它不适用于&lt;p&gt;。修复是'/&lt;(\w+)(&gt;|\s+[^&gt;]*&gt;)/'
    • &lt;img src="foo.jpg" label="&lt;what&gt;"/&gt; 上无法正常工作。
    • @CanSpice:那又怎样?不要让我教你怎么做!另外,除了数据,我们还知道什么吗?不。很有可能您不在数据中,这可能根本不是开放式的。
    • @tchrist:所以他应该使用 HTML 解析器来解析 HTML。他应该使用正确的工具来完成这项工作。
    • @CanSpice:我还没准备好这么说。我在编辑 HTML 时在vi 中使用搜索和替换。如果允许,那么当然应该允许您在 HTML 上使用模式匹配。如果您不被允许,那么您不应该被允许在这些文件上使用vi。 HTML 是文本——复杂的文本,我承认,但仍然只是文本。在vi 中写:/&lt;table&gt;/, /&lt;\/table&gt;/s/&lt;td&gt;/&lt;td color="#000000"&gt;/ 没有任何问题,因此用您选择的编程语言编写等价的也没有错。停止在非文本解决方案上推新手。
    【解决方案4】:

    在 python 中,一种解决方案是使用正则表达式在 html 中获取所有不同的标签名称。

    import re
    
    s = """<div id="someid">
           <img src="someurl" />
           <br />
           <p>some content</p>
           </div>
        """
    
    print(set(re.findall('<(\w+)', s)))
    # {'p', 'img', 'div', 'br'}
    or 
    print({i.replace('<', '') for i in re.findall('(<\w+)',s)})
    # {'p', 'img', 'div', 'br'}
    

    【讨论】:

      猜你喜欢
      • 2012-04-02
      • 1970-01-01
      • 2011-04-01
      • 2016-01-22
      • 1970-01-01
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多