我需要匹配所有这些开头标记:

<p>
<a href="foo">

但不是这些:

<br />
<hr class="foo" />

我想出了这个,想确保我做得对。我只是在捕捉a-z。

<([a-z]+) *[^/]*?>

我相信上面写着:

找到一个小于,然后查找(并捕获)a-z一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于

我有这个权利吗?更重要的是,你怎么看?


当前回答

Try:

<([^\s]+)(\s[^>]*?)?(?<!/)>

它与您的类似,但最后一个>不能在斜杠之后,也接受h1。

其他回答

虽然只有正则表达式的任意HTML是不可能的,但有时使用它们来解析一组有限的已知HTML是合适的。

如果您有一小组HTML页面,希望从中提取数据,然后将其填充到数据库中,正则表达式可能会很好地工作。例如,我最近想获得澳大利亚联邦代表的姓名、党派和选区,这是我从议会网站上得到的。这是一份有限的一次性工作。

Regexes对我来说很好,而且安装速度很快。

如果您只是试图查找这些标记(没有解析的野心),请尝试以下正则表达式:

/<[^/]*?>/g

我在30秒内写下了它,并在这里进行了测试:http://gskinner.com/RegExr/

它匹配您提到的标记类型,而忽略您所说的要忽略的类型。

关于解析(x)HTML的正则表达式方法的问题,所有提到一些限制的人的答案都是:你没有受过足够的训练来统治这一强大武器的力量,因为这里没有人谈到递归。

一位正则表达式不可知论的同事通知了我这次讨论,这肯定不是网络上第一次讨论这个古老而热门的话题。

在阅读了一些帖子后,我做的第一件事就是在这个线程中查找“?R”字符串。第二个是搜索“递归”。

不,天哪,找不到火柴。由于没有人提到解析器构建的主要机制,我很快就意识到没有人理解这一点。

如果(x)HTML解析器需要递归,那么仅使用没有递归的正则表达式解析器是不够的。这是一个简单的构造。

正则表达式的黑色艺术很难掌握,因此,在尝试和测试我们的个人解决方案以一手掌握整个网络时,我们可能还遗漏了其他可能性。。。嗯,我很确定:)

这是一个神奇的模式:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

试试看。它是以PHP字符串形式编写的,所以“s”修饰符使类包含换行符。

下面是我在一月份编写的PHP手册的示例注释:参考

(注意。在那个注释中,我错误地使用了“m”修饰符;它应该被删除,尽管它被正则表达式引擎丢弃,因为没有使用^或$锚定)。

现在,我们可以从一个更明智的角度来讨论这种方法的局限性:

根据正则表达式引擎的具体实现,递归在解析嵌套模式的数量上可能有限制,但这取决于所使用的语言尽管已损坏,(x)HTML不会导致严重错误。它没有经过消毒。

无论如何,它只是一个正则表达式模式,但它揭示了开发许多强大实现的可能性。

我编写这个模式是为了支持我在框架中构建的模板引擎的递归下降解析器,无论是在执行时间还是在内存使用方面,性能都非常出色(与使用相同语法的其他模板引擎无关)。

RegEx匹配除XHTML自包含标记之外的开放标记跳过所有其他标记(和内容)。


这个正则表达式就是这样做的。如果您只需要匹配特定的Open标记,请列出一个列表替换(?:p | br |<whatever tags you want>)并替换[\w:]+构造在下面的适当位置。

<(?:(?::(?):(script | style | object | embed | applet | noframes | noscript | noembed)(?:\s+(?>“[\s\s]*?”|'[\s\s]*?'|(?:?\s*>)[\s\s]*</\1\s*(?=>)(*跳过)(*失败))|(?:[\w:]+\b(?=((?:“[\s\s]*?”|'[\s\s]*?'|[^>]?)*)>)\2(?<!/))|/?)|\?[\S\S]*?\|(?:

https://regex101.com/r/uMvJn0/1

 # Mix html/xml     
 # https://regex101.com/r/uMvJn0/1     
 
 <
 (?:
    
    # Invisible content gets failed
    
    (?:
       (?:
                               # Invisible content; end tag req'd
          (                    # (1 start)
             script
           | style
           | object
           | embed
           | applet
           | noframes
           | noscript
           | noembed 
          )                    # (1 end)
          (?:
             \s+ 
             (?>
                " [\S\s]*? "
              | ' [\S\s]*? '
              | (?:
                   (?! /> )
                   [^>] 
                )?
             )+
          )?
          \s* >
       )
       
       [\S\s]*? </ \1 \s* 
       (?= > )
       (*SKIP)(*FAIL)
    )
    
  | 
    
    # This is any open html tag we will match
    
    (?:
       [\w:]+ \b 
       (?=
          (                    # (2 start)
             (?:
                " [\S\s]*? " 
              | ' [\S\s]*? ' 
              | [^>]? 
             )*
          )                    # (2 end)
          >
       )
       \2 
       (?<! / )
    )
    
  | 
    # All other tags get failed
    
    (?:
       (?: /? [\w:]+ \s* /? )
     | (?:
          [\w:]+ 
          \s+ 
          (?:
             " [\S\s]*? " 
           | ' [\S\s]*? ' 
           | [^>]? 
          )+
          \s* /?
       )
     | \? [\S\s]*? \?
     | (?:
          !
          (?:
             (?: DOCTYPE [\S\s]*? )
           | (?: \[CDATA\[ [\S\s]*? \]\] )
           | (?: -- [\S\s]*? -- )
           | (?: ATTLIST [\S\s]*? )
           | (?: ENTITY [\S\s]*? )
           | (?: ELEMENT [\S\s]*? )
          )
       )
    )
    (*SKIP)(*FAIL)
 )
 >

正如许多人已经指出的那样,HTML不是一种常规语言,这会使解析变得非常困难。我的解决方案是使用整洁的程序将其转换为常规语言,然后使用XML解析器来使用结果。有很多好的选择。我的程序使用Java和jtidy库编写,将HTML转换为XML,然后Jaxen将其展开为结果。