RegEx匹配开放标签,除了XHTML自包含标签

我需要匹配所有这些开放标签:

<p><a href="foo">

但不是这些:

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

我想到了这个,并想确保我是正确的。我只是捕捉a-z

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

我相信它说:

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

我有这个权利吗?更重要的是,你觉得呢?

3513639 次浏览

您希望第一个>前面没有/。有关如何做到这一点的详细信息,请查看这里。它被称为负后视。

但是,在这个示例文档中,一个简单的实现最终将匹配<bar/></foo>

<foo><bar/></foo>

你能提供更多关于你试图解决的问题的信息吗?你是否以编程方式迭代标签?

尝试:

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

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

你不能用正则表达式解析[X]超文本标记语言。因为超文本标记语言不能被正则表达式解析。正则表达式不是一个可以用来正确解析超文本标记语言的工具。正如我以前多次回答超文本标记语言和正则表达式的问题一样,正则表达式的使用不允许你使用超文本标记语言。正则表达式是一个不够复杂的工具,无法理解超文本标记语言所使用的结构。超文本标记语言不是正则表达式,因此不能被正则表达式解析。正则表达式查询不能将超文本标记语言分解成有意义的部分。很多次了,但它没有影响到我。即使是Perl使用的增强的不规则正则表达式也无法解析超文本标记语言。你永远不会让我崩溃。超文本标记语言是一种足够复杂的语言,它无法被正则表达式解析。即使是Jon Skeet也无法使用正则表达式解析超文本标记语言。每次你试图用正则表达式解析超文本标记语言时,邪恶的孩子都会流下处女的鲜血,俄罗斯黑客就会把你的网络应用程序偷走。用正则表达式解析文本超标记语言会召唤被污染的灵魂进入活人的领域。超文本标记语言和正则表达式就像爱情、婚姻和仪式杀婴一样相辅相成。

无法坚持下去已经太晚了。正则表达式和超文本标记语言在同一个概念空间中的力量会像许多水泥浆一样摧毁你的思想。如果你用正则表达式解析超文本标记语言,你就会屈服于它们及其亵渎神明的方式,这些方式注定我们所有人都要为那个名字不能在基本多语言平面上表达的人付出非人的辛劳,他来了。超文本标记语言-plus-regexp将在你观察的同时液化有情者的n erves,你的心灵在恐怖的冲击中枯萎。基于Rege的超文本标记语言解析器是杀死StackOverflow太迟了,太迟了,我们无法被拯救的癌症,一个chi的越轨确保了regex将消耗所有的活组织(除了超文本标记语言,它不能,如前所述)亲爱的上帝,帮助我们,任何人如何才能在这场灾难中幸存下来使用regex来解析超文本标记语言已经注定了人类永恒的恐惧折磨和安全漏洞使用regex作为处理超文本标记语言的工具建立了一个BREA在这个世界之间和c o破坏实体的可怕领域(如SGML实体,但更腐败)只是一瞥se的reg超文本标记语言的解析器的世界中不断地传输p罗格曼的意识n到world的不断尖叫,他来了 ͎a̧͈͖r̽̾̈́͒͑e0ithy regex感染wil ͎a̧͈͖r̽̾̈́͒͑e1 ML解析器,应用程序和存在的所有时间都像Visual Basic只会更糟 ͎a̧͈͖r̽̾̈́͒͑e2es ͎a̧͈͖r̽̾̈́͒͑e3 ght h ͎a̧͈͖r̽̾̈́͒͑e4的弧度是 ͎a̧͈͖r̽̾̈́͒͑e5ain,这首关于 ͎a̧͈͖r̽̾̈́͒͑e6的歌曲将在 ͎a̧͈͖r̽̾̈́͒͑e8 ͎a̧͈͖r̽̾̈́͒͑e9OST第太迟了,太迟了,我们无法被拯救0秒播出,他将在太迟了,太迟了,我们无法被拯救1太迟了,太迟了,我们无法被拯救2或太迟了,太迟了,我们无法被拯救3l我的FAC太迟了,太迟了,我们无法被拯救4太迟了,太迟了,我们无法被拯救5θ停止太迟了,太迟了,我们无法被拯救6e s ͎a̧͈͖r̽̾̈́͒͑e n太迟了,太迟了,我们无法被拯救7O͇̹̺ͅƝ̴ȳ̳ TH太迟了,太迟了,我们无法被拯救8S̨̥̫͎̭ͯ̿̔̀ͅ


您尝试过使用XML解析器吗?


主持人的说明

此帖子已被锁定,以防止对其内容进行不适当的编辑。该帖子看起来与预期的完全相同-其内容没有问题。请不要标记它以引起我们的注意。

我建议使用查询路径查询路径来解析PHP中的XML和超文本标记语言。它基本上与jQuery的语法相同,只是它在服务器端。

虽然只有正则表达式的任意超文本标记语言是不可能的,但有时使用它们来解析有限的,已知的超文本标记语言集是合适的。

如果你有一小套超文本标记语言页面,你想从中抓取数据,然后塞进数据库,正则表达式可能会很好。例如,我最近想获取澳大利亚联邦代表的姓名、政党和地区,我从议会的网站上得到了这些。这是一项有限的一次性工作。

正则表达式对我来说工作得很好,并且设置得非常快。

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

这是我用来匹配超文本标记语言的正则表达式:

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

它可能不完美,但我通过很多超文本标记语言运行此代码。请注意,它甚至会捕获像<a name="badgenerator"">这样出现在网络上的奇怪事物。

我想让它不匹配自包含的标签,你要么想使用科比的负面回溯:

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

或者把if和if not结合起来。

对于降级选民:这是来自实际产品的工作代码。我怀疑任何阅读此页面的人都会认为在超文本标记语言上使用正则表达式是社会可接受的。

警告:我应该注意到,这个正则表达式在存在CDATA块、注释、脚本和样式元素的情况下仍然会崩溃。好消息是,您可以摆脱那些使用正则表达式的…

<?php$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');
$html = '<p><a href="#">foo</a></p><hr/><br/><div>name</div>';
$dom = new DOMDocument();$dom->loadHTML($html);$els = $dom->getElementsByTagName('*');foreach ( $els as $el ) {$nodeName = strtolower($el->nodeName);if ( !in_array( $nodeName, $selfClosing ) ) {var_dump( $nodeName );}}

输出:

string(4) "html"string(4) "body"string(1) "p"string(1) "a"string(3) "div"

基本上只需定义自关闭的元素节点名称,将整个html字符串加载到DOM库中,获取所有元素,循环并过滤掉非自关闭的元素并对其进行操作。

我相信您现在已经知道您不应该为此目的使用regex。

在我看来,你试图匹配末尾没有“/”的标签。试试这个:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

我以前使用过一个名为HTMLParser的开源工具。它旨在以各种方式解析超文本标记语言,并且很好地服务于此目的。它可以将超文本标记语言解析为不同的treenode,您可以轻松使用其API从节点中获取属性。看看这是否可以帮助你。

如果您需要PHP:

除非XML格式正确,否则PHP DOM功能将无法正常工作。无论它们的使用对其他人有多好。

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

我从来没有使用过查询路径,所以不能评论它的有用性。

另一个要尝试的是我的DOMParser,它对资源非常轻,我已经愉快地使用了一段时间。简单易学,功能强大。

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

我只在XML解析器被证明无法承受实际使用时才编写我的类。宗教的否决只会阻止发布有用的答案-请保持问题的角度。

我不知道您的确切需求,但如果您也使用. NET,您不能使用Html敏捷包吗?

摘录:

它是一个. NET代码库,允许你解析"out of the web"超文本标记语言文件。解析器非常宽容“真实世界”畸形超文本标记语言。

每当我需要从超文本标记语言文档中快速提取内容时,我都会使用Tidy将其转换为XML,然后使用XPath或XSLT来获取我需要的内容。在你的情况下,像这样:

//p/a[@href='foo']

我认为这里的缺陷是超文本标记语言是乔姆斯基类型2文法(上下文无关文法),正则表达式是乔姆斯基类型3文法(正规文法)。由于类型2语法从根本上比类型3语法更复杂(参见乔姆斯基等级制度),您不可能做到这一点。

但许多人会尝试,有些人甚至会声称成功-但直到其他人发现错误并完全搞砸你。

虽然你不能用正则表达式解析超文本标记语言的答案是正确的,但它们不适用于这里。OP只想用正则表达式解析一个超文本标记语言,这是可以用正则表达式完成的事情。

但是,建议的regex是错误的:

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

如果您向正则表达式添加了一些内容,通过回溯可以强制它匹配像<a >>这样的愚蠢的东西,[^/]太宽松了。还要注意,<space>*[^/]*是多余的,因为[^/]*也可以匹配空格。

我的建议是

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

其中(?<! ... )是(在Perl正则表达式中)否定的回溯。它读取“a<,然后是一个单词,然后是任何不是>的东西,最后一个可能不是/,然后是>”。

请注意,这允许像<a/ >这样的东西(就像原始正则表达式一样),因此如果您想要更具限制性的东西,您需要构建一个正则表达式来匹配由空格分隔的属性对。

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

虽然使用正则表达式并不适合和有效,但有时正则表达式为简单的匹配问题提供了快速解决方案,在我看来,将正则表达式用于琐碎的工作并不可怕。

有一个关于匹配最里面的超文本标记语言元素的权威博客文章,由Steven Levithan编写。

在shell中,您可以使用ed解析超文本标记语言

  1. Turing.sed
  2. 编写超文本标记语言解析器(家庭作业)
  3. ????
  4. 利润!

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

这可能会:

<.*?[^/]>

或者没有结尾标签:

<[^/].*?[^/]>

超文本标记语言解析器上的战火是怎么回事?超文本标记语言解析器必须解析(并重建!)整个文档,然后才能对您的搜索进行分类。正则表达式在某些情况下可能更快/更优雅。我的2美分…

有一些用BBCode这里替换超文本标记语言的不错的正则表达式。对于所有反对者,请注意,他并没有试图完全解析超文本标记语言,只是为了净化它。他可能有能力杀死他简单的“解析器”无法理解的标签。

例如:

$store =~ s/http:/http:\/\//gi;$store =~ s/https:/https:\/\//gi;$baseurl = $store;
if (!$query->param("ascii")) {$html =~ s/\s\s+/\n/gi;$html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;}
$html =~ s/\n//gi;$html =~ s/\r\r//gi;$html =~ s/$baseurl//gi;$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;$html =~ s/<p>/\n\n/gi;$html =~ s/<br(.*?)>/\n/gi;$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;$html =~ s/<link(.*?)>//gi;$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;$html =~ s/<ul(.*?)>/\[list]/gi;$html =~ s/<\/ul>/\[\/list]/gi;$html =~ s/<div>/\n/gi;$html =~ s/<\/div>/\n/gi;$html =~ s/<td(.*?)>/ /gi;$html =~ s/<tr(.*?)>/\n/gi;
$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;
$html =~ s/<head>(.*?)<\/head>//sgmi;$html =~ s/<object>(.*?)<\/object>//sgmi;$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;$html =~ s/<title>(.*?)<\/title>//sgmi;$html =~ s/<!--(.*?)-->/\n/sgmi;
$html =~ s/\/\//\//gi;$html =~ s/http:\//http:\/\//gi;$html =~ s/https:\//https:\/\//gi;
$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;$html =~ s/\r\r//gi;$html =~ s/\[img]\//\[img]/gi;$html =~ s/\[url=\//\[url=/gi;

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

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

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

不,天哪,没有找到匹配。由于没有人提到解析器的主要机制,我很快意识到没有人明白这一点。

如果一个(x)超文本标记语言解析器需要递归,一个没有递归的正则表达式解析器是不够的。

正则表达式的黑魔法很难掌握,所以也许我们在尝试和测试我们的个人解决方案时遗漏了更多的可能性,以一方面捕获整个网络……好吧,我敢肯定:)

这是魔法模式:

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

试试看。它是作为PHP字符串编写的,因此“s”修饰符使类包含换行符。

这是我在一月份写的PHP手册上的示例说明参考

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

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

  1. 根据正则表达式引擎的具体实现,递归在解析的嵌套模式数中可能会有限制,但这取决于使用的语言
  2. 虽然损坏了,(x)超文本标记语言不会造成严重错误。它不是消毒

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

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

如果您只想要标记名称,则应该可以通过正则表达式来实现。

<([a-zA-Z]+)(?:[^>]*[^/] *)?>

但我认为“莫里茨”的解决方案已经很好了。我一开始没有看到它。

在某些情况下,使用正则表达式是有意义的,因为它可能是最简单和最快的解决方案。

但正则表达式可以是一个非常强大的工具,当你有一个超文本标记语言的子集,你知道格式,你只是想提取一些值。

OP似乎没有说明他需要对标签做什么。例如,他需要提取内部文本,还是只是检查标签?

我坚定地认为正则表达式不是万能的、最终的文本解析器。我写了大量的文本解析代码,包括这段代码解析超文本标记语言

虽然我对正则表达式并不是那么擅长,但我认为正则表达式对于这种解析来说太死板和难以维护。

有些人会告诉你地球是圆的(或者如果他们想用奇怪的词说地球是一个扁圆的球体)。他们在撒谎。

有些人会告诉你正则表达式不应该是递归的。他们在限制你。他们需要征服你,他们这样做是为了让你保持无知。

你可以生活在他们的现实中,或者服用红色药丸。

就像马歇尔勋爵(他是马歇尔. NET类的亲戚吗?),我看过Underverse基于堆栈的正则表达式,并带着你无法想象的权力知识回来了。是的,我想有一两个老人在保护他们,但他们正在看电视上的足球,所以这并不难。

我认为XML的情况很简单。RegEx(在. NET语法中),用bas64压缩并编码,以使您的弱智更容易理解,应该是这样的:

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

要设置的选项是RegexOptions.ExplicitCapture。您要查找的捕获组是ELEMENTNAME。如果捕获组ERROR不为空,则出现解析错误并且正则表达式停止。

如果您在将其重新转换为人类可读的正则表达式时遇到问题,这应该有助于:

static string FromBase64(string str){byte[] byteArray = Convert.FromBase64String(str);
using (var msIn = new MemoryStream(byteArray))using (var msOut = new MemoryStream()) {using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {ds.CopyTo(msOut);}
return Encoding.UTF8.GetString(msOut.ToArray());}}

如果你不确定,不,我不是在开玩笑(但也许我在撒谎)。它会起作用的。我已经构建了大量的单元测试来测试它,我甚至使用了(部分)一致性测试。它是一个标记器,不是一个成熟的解析器,所以它只会将XML拆分为它的组件标记。它不会解析/集成DTD。

哦…如果你想要正则表达式的源代码,还有一些辅助方法:

regex来标记xml完整的普通正则表达式

我喜欢用正则表达式解析超文本标记语言。我不会尝试解析故意破坏的白痴超文本标记语言。这段代码是我的主要解析器(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

它被称为html拆分,将超文本标记语言分成几行,每行有一个标签或文本块。然后可以使用其他文本工具和脚本进一步处理这些行,例如greped,Perl等。我甚至不是在开玩笑:)享受。

如果您希望处理大量网页,将我的slurp-ally-first Perl脚本重新调整为一个不错的流式处理很简单。但这并不是真的必要。

超文本标记语言拆分


一些更好的正则表达式:

/(<.*?>|[^<]+)\s*/g    # Get tags and text/(\w+)="(.*?)"/g       # Get attibutes

它们适用于XML/XHTML。

通过较小的变化,它可以处理混乱的超文本标记语言……或者首先转换超文本标记语言->XHTML。


编写正则表达式的最佳方式是Lex/Yacc风格,而不是不透明的单行代码或注释的多行怪物。

以下是解决方案:

<?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]);?>

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




我还输入了标签:

  1. 一个属性
  2. 不止一个属性
  3. 值绑定到单引号双引号的属性
  4. 当分隔符是双引号时包含单引号的属性,反之亦然
  5. “不漂亮”属性在“=”符号之前、之后以及之前和之后都有空格。

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

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

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

用户@ridgerunner注意到该模式不允许无引号属性无值属性。在这种情况下,微调为我们带来以下模式:

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

了解模式

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

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

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

我同意解析XML和特别是超文本标记语言的正确工具是解析器而不是正则表达式引擎。然而,就像其他人指出的那样,如果您知道数据格式,有时使用正则表达式会更快、更容易,并且可以完成工作。

微软实际上有一部分. NET Framework中正则表达式的最佳实践,并专门谈论考虑[ing]输入源

正则表达式确实有局限性,但您是否考虑过以下问题?

. NET框架在正则表达式方面是独一无二的,因为它支持平衡组定义

出于这个原因,我相信您可以使用正则表达式解析XML。但是请注意,它必须是有效的XML浏览器非常宽容超文本标记语言,允许在超文本标记语言中使用糟糕的XML语法)。这是可能的,因为“平衡组定义”将允许正则表达式引擎充当PDA。

引自上述第1条:

. NET正则表达式引擎

如上所述,正确平衡的结构不能用正则表达式。但是,. NET正则表达式引擎提供了一些允许平衡构造的构造已确认

  • (?<group>)-将捕获的结果推送到捕获堆栈上名称组。
  • (?<-group>)-弹出名称组关闭的顶部捕获捕获堆栈。
  • (?(group)yes|no)-如果存在组,则匹配yes部分否则不匹配任何部分。

这些构造允许. NET正则表达式模拟通过本质上允许堆栈的简单版本来限制PDA操作:推送、弹出和清空。简单的操作几乎等效于递增,递减和比较分别为零。这允许. NET正则表达式引擎识别上下文无关语言的子集,特别是那些仅需要一个简单的计数器。这反过来又允许非传统. NET正则表达式来识别正确平衡的个体结构。

考虑以下正则表达式:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)(?><!-- .*? -->                  |<[^>]*/>                      |(?<opentag><(?!/)[^>]*[^/]>)  |(?<-opentag></[^>]*[^/]>)     |[^<>]*)*(?(opentag)(?!))

使用旗帜:

  • 单线
  • IgnorePatternWhitesspace(如果折叠regex并删除所有空格,则不需要)
  • IgnoreCase(不需要)

正则表达式解释(内联)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...(?>                                        # atomic group / don't backtrack (faster)<!-- .*? -->                 |          # match xml / html comment<[^>]*/>                     |          # self closing tag(?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag(?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag[^<>]*                                  # something between tags)*                                         # match as many xml tags as possible(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

你可以试试这个一个更好的. NET正则表达式测试器

我使用了以下示例来源:

<html><body><div><br /><ul id="matchMe" type="square"><li>stuff...</li><li>more stuff</li><li><div><span>still more</span><ul><li>Another &gt;ul&lt;, oh my!</li><li>...</li></ul></div></li></ul></div></body></html>

这找到了匹配项:

   <ul id="matchMe" type="square"><li>stuff...</li><li>more stuff</li><li><div><span>still more</span><ul><li>Another &gt;ul&lt;, oh my!</li><li>...</li></ul></div></li></ul>

虽然它实际上是这样出来的:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

最后,我真的很喜欢杰夫·阿特伍德的文章:解析Html The Cthulhu Way。有趣的是,它引用了这个问题的答案,目前有超过4k票。

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

各部分解释说:

<:起始字符

\s*:标签名称前可能有空格(丑陋,但可能)。

(\w+):标签可以包含字母和数字(h1)。好吧,\w也匹配'_',但我猜它并没有伤害。如果好奇,请使用([a-zA-Z0-9]+)代替。

[^/>]*:除了>/之外的任何东西,直到关闭>

>:关闭>

无关

对于那些低估正则表达式的人,他们说它们只和正则表达式一样强大:

nbanban不是规则的,甚至不是上下文无关的,可以与^(a+)b\1b\1$匹配

反向引用FTW

中国古代战略家、将军、哲学家孙子说:

有人说,如果你了解你的敌人,了解你自己,你可以赢得一百场战斗而不会有一次失败。如果你只了解你自己,而不了解你的对手,你可能会赢,也可能会输。如果你既不了解你自己,也不了解你的敌人,你总是会危及自己。

在这种情况下,你的敌人是超文本标记语言,你要么是你自己,要么是正则表达式。你甚至可能是具有不规则正则表达式的Perl。了解超文本标记语言。了解你自己。

我写了一篇描述超文本标记语言本质的黑句。

HTML hascomplexity exceedingregular language.

我还写了一篇描述Perl中regex性质的haiku。

The regex you seekis defined within the phrase<([a-zA-Z]+)(?:[^>]*[^/]*)?>

我想这可能有用

<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>

这可以测试这里


根据w3学校

XML命名规则

XML元素必须遵循以下命名规则:

  • 名称可以包含字母、数字和其他字符
  • 名称不能以数字或标点符号开头
  • 名称不能以字母xml(或xmlxml等)开头。
  • 名称不能包含空格
  • 可以使用任何名称,并且不保留任何单词。

我使用的模式将遵守这些规则。

诚然,在编程时,在处理超文本标记语言时,通常最好使用专用的解析器和API而不是正则表达式,特别是在准确性至关重要的情况下(例如,如果你的处理可能有安全隐患)。然而,我不认为XML风格的标记永远不应该用正则表达式处理的教条观点。在某些情况下,正则表达式是这项工作的一个很好的工具,例如在文本编辑器中进行一次性编辑、修复损坏的XML文件或处理看起来像但不完全是XML的文件格式时。有一些问题需要注意,但它们不是不可克服的,甚至不一定相关。

在我刚才提到的情况下,像<([^>"']|"[^"]*"|'[^']*')*>这样的简单正则表达式通常就足够了。考虑到所有因素,这是一个天真的解决方案,但它确实正确地允许在属性值中使用未编码的>符号。如果您正在寻找,例如,table标记,您可以将其调整为</?table\b([^>"']|"[^"]*"|'[^']*')*>

只是为了给一个更“高级”的超文本标记语言regex看起来像什么,下面做了一个相当体面的工作,模拟现实世界的浏览器行为和HTML5解析算法:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

以下匹配XML标记的相当严格的定义(尽管它没有考虑XML名称中允许的全部Unicode字符):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

当然,这些不考虑周围的上下文和一些边缘情况,但即使是这样的事情也可以处理,如果你真的想(例如,通过在另一个正则表达式的匹配之间搜索)。

在一天结束时,使用最适合该作业的工具,即使该工具恰好是正则表达式。

如果你只是想找到这些标签(没有解析的野心),试试这个正则表达式:

/<[^/]*?>/g

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

它匹配您提到的标签类型,同时忽略您说要忽略的类型。

这是一个用于XML/XHTML的PCRE正则表达式,由简化的EBNF语法定义构建:

/(?(DEFINE)(?<tag> (?&tagempty) | (?&tagopen) ((?&textnode) | (?&tag) | (?&comment))* (?&tagclose))(?<tagunnested> (?&tagempty) | (?&tagopen) ((?&textnode) | (?&comment))* (?&tagclose))(?<textnode> [^<>]+)(?<comment> <!--([\s\S]*?)-->)(?<tagopen> < (?&tagname) (?&attrlist)? (?&ws)* >)(?<tagempty> < (?&tagname) (?&ws)* (?&attrlist)? (?&ws)* \/>)(?<tagclose> <\/ (?&tagname) (?&ws)* >)(?<attrlist> ((?&ws)+ (?&attr))+)(?<attr> (?&attrunquoted) | (?&attrsinglequoted) | (?&attrdoublequoted) | (?&attrempty))(?<attrempty> (?&attrname))(?<attrunquoted> (?&attrname) (?&ws)* = (?&ws)* (?&attrunquotedvalue))(?<attrsinglequoted> (?&attrname) (?&ws)* = (?&ws)* ' (?&attrsinglequotedvalue) ')(?<attrdoublequoted> (?&attrname) (?&ws)* = (?&ws)* " (?&attrdoublequotedvalue) ")(?<tagname> (?&alphabets) ((?&alphabets) | (?&digits))*)(?<attrname>(?&alphabets)+((?&alphabets)|(?&digits)|[:-]) )(?<attrunquotedvalue> [^\s"'=<>`]+)(?<attrsinglequotedvalue> [^']+)(?<attrdoublequotedvalue> [^"]+)(?<alphabets> [a-zA-Z])(?<digits> [0-9])(?<ws> \s))(?&tagopen)/x

这说明了如何构建上下文无关文法的正则表达式。您可以通过将最后一行的匹配从(?&tagopen)更改为(?&tagunnested)来匹配定义的其他部分。

真正的问题是:你应该这样做吗?

对于XML/XHTML,共识是

归功于nikic提供了这个想法。

RegEx匹配打开标签,除了XHTML自包含标签
跳过所有其他标签(和内容)。


这个正则表达式可以做到这一点。如果您只需要匹配特定的打开标记,请列出
在替换(?: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*(?=>)(*SKIP)(*FAIL))|(?:[\w:]+\b(?=((?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)*)>)\2(?<!/))|(?:(?:/?[\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))>

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))>

首先,回答一个直接的问题:您的正则表达式有一个bug,因为它会排除任何带斜杠的标签,而不仅仅是在末尾。例如,它会排除这个有效的开始标签:<a href="foo/bar.html">,因为它在属性值中有一个斜杠。

我们可以解决这个问题,但更严重的是,这个regex会导致误报,因为它也会匹配注释和cdata部分,其中相同的字符不代表有效的标记。例如:

<!-- <foo> -->

<![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)

这仅适用于符合超文本标记语言兼容性指南的XHTML。如果您想处理任意XHTML,您还应该处理处理指令和内部DTD,因为它们也可能嵌入误报。如果您还想处理超文本标记语言,则会有额外的复杂性,例如<script>标记。如果您还想处理无效超文本标记语言,则会变得更加复杂。

考虑到复杂性,我不建议走这条路。相反,寻找一个现成的(X)超文本标记语言解析库来解决您的问题。

解析器通常使用正则表达式(或类似的)将文档拆分为“标记”(doctype,开始标记,结束标记,文本内容等)。但是其他人会为您调试和测试这些正则表达式!根据解析器的类型,它可以通过匹配开始标记到结束标记来进一步构建元素的树结构。这几乎肯定会为您节省大量时间。

要使用的确切解析器库取决于您的语言和平台以及您要解决的任务。如果您需要访问实际的标记子字符串(例如,如果您正在为超文本标记语言编写语法荧光笔),您需要使用直接公开语法标记的SAX样式解析器。

如果您只是为了手动构建元素语法树而执行标签匹配,那么DOM解析器会为您完成这项工作。但是DOM解析器不会公开底层标签语法,因此不会解决您描述的确切问题。

您还应该考虑是否需要解析无效超文本标记语言。这是一项复杂得多的任务,但在狂野的网络上,大多数超文本标记语言实际上是无效的。像Pyton html5lib这样的东西可以解析无效的超文本标记语言。