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

<p>
<a href="foo">

但不是这些:

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

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

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

我相信上面写着:

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

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


当前回答

在shell中,可以使用sed解析HTML:

图灵.sed编写HTML解析器(作业)???利润


相关(为什么不应该使用正则表达式匹配):

如果你这么喜欢正则表达式,为什么不嫁给它们?正则表达式:现在有两个问题黑客攻击stackoverflow.com的HTML清理器

其他回答

免责声明:如果您有选择,请使用解析器。那是说。。。

这是我使用(!)匹配HTML标记的正则表达式:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

它可能并不完美,但我通过大量HTML运行了这段代码。注意,它甚至会捕捉到一些奇怪的东西,比如出现在网络上的<a name=“badgenerator”“>。

我想,为了使其不匹配自包含的标签,您可能需要使用Kobi的负面外观:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

或者如果没有,就合并。

对于下选民:这是从实际产品中工作的代码。我怀疑任何阅读此页面的人都会觉得在HTML上使用正则表达式是社会可以接受的。

注意:我应该注意,这个正则表达式在CDATA块、注释以及脚本和样式元素的存在下仍然会崩溃。好消息是,你可以去掉那些使用正则表达式的。。。

我建议在PHP中使用QueryPath解析XML和HTML。它的语法与jQuery基本相同,只是在服务器端。

我认为这里的缺陷是HTML是Chomsky Type 2语法(上下文无关语法),正则表达式是ChomskyType 3语法(正则语法)。由于第2类语法从根本上比第3类语法更复杂(请参见乔姆斯基层次结构),因此不可能实现这一点。

但很多人会尝试,有些人甚至会声称成功,但直到其他人发现错误并将你彻底搞砸。

如果PHP需要此功能:

除非是正确格式化的XML,否则PHP DOM函数将无法正常工作。不管它们对人类其他人的用处有多大。

simplehtmldom很好,但我发现它有点bug,而且内存很重[会在大页面上崩溃。]

我从未使用过querypath,所以不能评论它的有用性。

另一个可以尝试的是我的DOMParser,它非常节省资源,我已经很高兴地使用了一段时间。简单易学,功能强大。

对于Python和Java,发布了类似的链接。

对于反对者,我只在XML解析器无法承受实际使用时才编写了我的课程。宗教上的反对投票只是阻止了有用的答案被发布——请把事情放在问题的角度之内。

解决方案如下:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

为了深入测试,我输入了字符串自动关闭标记,如:

<hr/><br/><br>

我还输入了标记:

一个属性多个属性值绑定到单引号或双引号的属性分隔符为双引号时包含单引号的属性,反之亦然在“=”符号之前、之后以及前后都有空格的“unputy”属性。

如果你在上面的概念证明中发现了不起作用的东西,我可以分析代码来提高我的技能。

<编辑>我忘记了用户的问题是避免解析自动关闭标签。在这种情况下,模式更简单,变为:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

用户@ridgerunner注意到,该模式不允许未加引号的属性或没有值的属性。在这种情况下,微调会带来以下模式:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</EDIT>

了解模式

如果有人有兴趣了解更多有关模式的信息,我会提供一些提示:

第一个子表达式(\w+)与标记名匹配第二个子表达式包含属性的模式。其组成如下:一个或多个空白区+属性的名称(\w+)零个或多个空格\s*(是否可能,此处留空)“=”符号同样,零个或多个空白属性值的分隔符,单引号或双引号(“|”)。在模式中,单引号被转义,因为它与PHP字符串分隔符重合。此子表达式用括号捕获,因此可以再次引用它来解析属性的闭包,这就是为什么它非常重要的原因。属性的值,几乎可以匹配:(.*?);在这个特定的语法中,使用贪婪匹配(星号后面的问号),RegExp引擎启用了一个类似“向前看”的运算符,它匹配除此子表达式后面的内容以外的任何内容有趣的是:\4部分是一个backreference运算符,它指的是模式中之前定义的子表达式,在本例中,我指的是第四个子表达式,它是找到的第一个属性分隔符零个或多个空格*属性子表达式在这里结束,指定了零个或多个可能出现的事件,用星号表示。然后,由于标记可能以“>”符号之前的空白结尾,因此零个或更多的空白与\s*子模式匹配。要匹配的标记可能以一个简单的“>”符号结尾,也可能以XHTML闭包结尾,这使用了前面的斜杠:(/>|>)。当然,斜线是转义的,因为它与正则表达式分隔符重合。

小提示:为了更好地分析这段代码,有必要查看生成的源代码,因为我没有提供任何HTML特殊字符转义。