我需要匹配所有这些开头标记:
<p>
<a href="foo">
但不是这些:
<br />
<hr class="foo" />
我想出了这个,想确保我做得对。我只是在捕捉a-z。
<([a-z]+) *[^/]*?>
我相信上面写着:
找到一个小于,然后查找(并捕获)a-z一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
我需要匹配所有这些开头标记:
<p>
<a href="foo">
但不是这些:
<br />
<hr class="foo" />
我想出了这个,想确保我做得对。我只是在捕捉a-z。
<([a-z]+) *[^/]*?>
我相信上面写着:
找到一个小于,然后查找(并捕获)a-z一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
当前回答
首先,回答一个直接的问题:正则表达式有一个bug,因为它会在任何地方排除带有斜线的标记,而不仅仅是在结尾。例如,它将排除这个有效的开头标记:<a href=“foo/bar.html”>,因为它在属性值中有一个斜杠。
我们可以解决这个问题,但更严重的是,这个正则表达式将导致误报,因为它还将匹配内部注释和cdata部分,其中相同的字符不表示有效的标记。例如:
<!-- <foo> -->
or
<![CDATA[ <foo> ]]>
尤其是嵌入脚本中的html字符串很可能会触发误报,JavaScript中经常使用<和>作为比较运算符也是如此。当然还有html的部分,这些部分用<!-->注释掉了。
因此,为了只匹配实际标记,您还需要能够跳过过去的注释和cdata部分。因此,您需要正则表达式来匹配注释和cdata,但只捕获开头标记。这仍然可以使用rexep,但它变得更加复杂,例如:
(
<!-- .*? --> # comment
| <!\[CDATA\[ .*? \]\]> # CData section
| < \w+ ( "" [^""]* "" | ' [^']* ' | [^>/'""] )* /> # self-closing tag
| (?<tag> < \w+ ( "" [^""]* "" | ' [^']* ' | [^>/'""] )* > ) # opening tag - captured
| </ \w+ \s* > # end tag
)
这仅适用于符合HTML兼容性准则的XHTML。如果您想处理任意XHTML,还应该处理处理指令和内部DTD,因为它们也可以嵌入误报。如果您还想处理HTML,还有其他复杂的问题,比如<script>-标记。如果您还想处理无效的HTML,则会变得更加复杂。
鉴于复杂性,我不建议走这条路。相反,寻找一个现成的(X)HTML解析库,它可以解决您的问题。
解析器通常在后台使用正则表达式(或类似的表达式)将文档拆分为“标记”(doctype、开始标记、结束标记、文本内容等)。但其他人会为您调试和测试这些正则表达式!根据解析器的类型,它可以通过匹配开始标记和结束标记来进一步构建元素的树结构。这几乎肯定会为您节省大量时间。
要使用的精确解析器库取决于您的语言和平台以及您正在解决的任务。如果您需要访问实际的标记子字符串(例如,如果您正在为HTML编写语法高亮),则需要使用SAX样式的解析器,该解析器直接公开语法标记。
如果您只执行标记匹配以手动构建元素的语法树,那么DOM解析器将为您完成这项工作。但是DOM解析器不公开底层的标记语法,因此无法解决您描述的确切问题。
您还应该考虑是否需要解析无效的HTML。这是一项复杂得多的任务,但在野生网络上,大多数HTML实际上是无效的。类似Pytons html5lib的东西可以解析无效的HTML。
其他回答
如果您只是试图查找这些标记(没有解析的野心),请尝试以下正则表达式:
/<[^/]*?>/g
我在30秒内写下了它,并在这里进行了测试:http://gskinner.com/RegExr/
它匹配您提到的标记类型,而忽略您所说的要忽略的类型。
我想这可能有用
<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>
这可以在这里进行测试。
根据W3学校。。。
XML命名规则
XML元素必须遵循以下命名规则:
名称可以包含字母、数字和其他字符名称不能以数字或标点字符开头名称不能以字母xml(或xml、xml等)开头名称不能包含空格可以使用任何名称,不保留任何单词。
我使用的模式将遵循这些规则。
<([a-z][^>\s]*)(?:\s+[^>]+)?>
此正则表达式将匹配由单个单词(例如<p>、<a>等)组成的开头标记,后跟零个或多个空格和任意数量的字符(除了>或空白),然后是结束>字符。它还会将标记与属性匹配,并且不会将标记与包含a-z以外字符的名称匹配。然而,它仍然不会匹配自动关闭标签。
我喜欢用正则表达式解析HTML。我不会试图解析故意破坏的白痴HTML。这段代码是我的主解析器(Perl版):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print
它被称为htmlsplit,将HTML拆分为多行,每行上有一个标记或文本块。然后可以使用其他文本工具和脚本(如grep、sed、Perl等)进一步处理这些行。
如果您希望处理巨大的网页,将我的slurp一切第一个Perl脚本重新设置为一个不错的流式处理就足够简单了。但这不是真的必要。
HTML拆分
一些更好的正则表达式:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
它们适用于XML/XTML。
通过小的变化,它可以处理杂乱的HTML。。。或者先转换HTML->XHTML。
编写正则表达式的最佳方式是使用Lex/Yacc样式,而不是不透明的单行或注释的多行怪物。我还没有在这里这样做;这些人几乎不需要它。
的确,在编程时,在处理HTML时,最好使用专用解析器和API,而不是正则表达式,尤其是在准确性至关重要的情况下(例如,如果您的处理可能涉及安全问题)。然而,我并不认为XML风格的标记永远不应该用正则表达式来处理。在某些情况下,正则表达式是一个很好的工具,例如在文本编辑器中进行一次性编辑,修复损坏的XML文件,或者处理看起来像但不太像XML的文件格式。有一些问题需要注意,但它们并非不可逾越,甚至不一定相关。
像<([^>“']|”[^“]*”|'[^']*')*>这样的简单正则表达式通常足够好,例如我刚才提到的那些情况。这是一个天真的解决方案,但它确实允许在属性值中使用未编码的>符号。如果您正在查找,例如,表标签,您可以将其调整为</?表\b([^>“']|”[^“]*”|'[^']*')*>。
为了了解更“高级”的HTML正则表达式会是什么样子,以下内容在模拟真实世界的浏览器行为和HTML5解析算法方面做了相当出色的工作:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
以下内容与相当严格的XML标记定义相匹配(尽管它没有考虑XML名称中允许的全部Unicode字符集):
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
当然,这些不考虑周围的上下文和一些边缘情况,但如果您真的想处理这些问题(例如,通过在另一个正则表达式的匹配项之间进行搜索),也可以处理这些问题。
在一天结束时,使用最适合该作业的工具,即使该工具恰好是正则表达式。