HTML标签中用于语法高亮属性的正则表达式

8

我正在为Sublime/TextMate语言文件编写正则表达式以进行语法高亮,它要求我在非自闭合的 HTML 标签上“开始”,并在相应的闭合标签上“结束”:

  • 开始: (<)([a-zA-Z0-9:.]+)[^/>]*(>)

  • 结束: (</)(\2)([^>]*>)

到目前为止,一切都很好,我能够捕获标签名称,并匹配以便能够应用适当的模式来处理标签之间的区域。

jsx-tag-area:
    begin: (<)([a-zA-Z0-9:.]+)[^/>]*>
    beginCaptures:
      '1': {name: punctuation.definition.tag.begin.jsx}
      '2': {name: entity.name.tag.jsx}
    end: (</)(\2)([^>]*>)
    endCaptures:
      '1': {name: punctuation.definition.tag.begin.jsx}
      '2': {name: entity.name.tag.jsx}
      '3': {name: punctuation.definition.tag.end.jsx}
    name: jsx.tag-area.jsx
    patterns:
    - {include: '#jsx'}
    - {include: '#jsx-evaluated-code'}

现在我还希望能够捕获开标签中零个或多个html属性,以便能够突出显示它们。
所以如果标签是<div attr="Something" data-attr="test" data-foo> 它将匹配attr, data-attr, 和 data-foo, 以及 <div 类似于(这只是初步的): (<)([a-zA-Z0-9:.]+)(?:\s(?:([0-9a-zA-Z_-]*=?))\s?)*)[^/>]*(>) 它不必非常完美,只是为了一些语法高亮而已,但我很难弄清如何在标签内实现多个捕获组,是否应该使用环视等,或者这是否可以通过单个表达式实现。
编辑:下面是有关特定情况/问题的更多详细信息 - https://github.com/reactjs/sublime-react/issues/18

3
如果您试图捕获任意数量的属性,这种方法可能不会很有效。如果属性数量是可变的,那么正则表达式会变得非常混乱和难以阅读。这是捕获两个属性时的正则表达式,看起来非常丑陋 - skamazin
2
你看过这个关于正则表达式的问题吗?RegEx match open tags except XHTML self-contained tags - Bergi
1
当然可以 :) 我不是在尝试精确解析HTML,而是在尝试粗略地模式匹配它...看一下使用案例https://github.com/reactjs/sublime-react/issues/18 - tgriesser
1
真遗憾我不能真正地玩这个...从教程来看,似乎可以使用"include": "$self"进行递归匹配,这非常可爱。它也可以用于特定的组吗?例如:匹配<[Tag][All Attributes]>...</[Tag]>,然后使用另一条规则解析[All Attributes]吗? - Kobi
我不知道JSX是什么,但您是否已经查看了http://examples.oreilly.com/0636920023630/Regex_Cookbook_2_Code_Samples.html,以防您可以将其示例翻译成此代码? (搜索“HTML标签(严格)”) - alexandroid
显示剩余2条评论
4个回答

1

我可能找到了一种可能的解决方案。

这并不完美,因为正如@skamazin在评论中所说,如果你试图捕获任意数量的属性,你将不得不重复匹配属性的模式多次,以限制你允许的属性数量。

正则表达式看起来很吓人,但它可能适合你的目标。也许可以简化它,或者你将不得不调整一些东西。

对于只有一个属性,它将是这样的:

(<)([a-zA-Z0-9:.]+)(?:(?: ((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))

演示

如果你需要更多的属性,你需要根据自己的需求添加这个标签:

(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))?

所以举个例子,如果你想允许最多3个属性,你的正则表达式将会是这样的:
(<)([a-zA-Z0-9:.]+)(?:(?: ((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)?(?: |>))?(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)?(?: |>))?

演示

如果适合您,需要进一步了解,请告诉我。


据我理解,不允许使用 g 修饰符。 - revo
好的,g修饰符仅用于演示多种情况,但我猜如果他发布的原始正则表达式已经起作用了,那么它就不是必需的了(<)([a-zA-Z0-9:.]+)[^/>]*(>)。 - Oscar Hermosilla

0

你自己的正则表达式对回答你的问题非常有帮助。

这个对我来说似乎很有效:

/(:?<|<\/)([a-zA-Z0-9:.]+)(?:\s(?:([0-9a-zA-Z_-]*=?))\s?)*[^/>]*(:?>|\/>)/g

开头和结尾的/只是正则表达式通常需要的包装器。此外,结尾的g代表全局匹配,因此它也适用于重复。

我使用的一个很好的工具来找出我的正则表达式错误是:http://regexr.com/

希望这有所帮助!


0

正则表达式似乎不够好,但由于您在使用sublime的脚本,有一种方法可以简化代码和流程。请记住,我是vim用户,不熟悉sublime的内部结构 - 而且,我通常使用javascript正则表达式,而不是PCRE(这似乎是sublime使用的格式,或者最接近的格式)。

思路如下:

  • 使用正则表达式获取标签、属性(以字符串形式)和标签内容
  • 使用捕获组进行进一步处理和匹配(如果需要)

在这种情况下,我制作了这个正则表达式:

<([a-z]+)\ ?([a-z]+=\".*?\"\ ?)?>([.\n\sa-z]*)(<\/\1>)?

它首先查找一个开放标签,为标签名称创建一个控制组,如果它发现一个空格,它会继续匹配大部分属性(在“...”模式内,我可以使用“[^\"]*?”仅匹配非引号字符,但我故意贪婪地匹配任何字符直到关闭引号 - 这是为了匹配大部分属性,我们稍后可以处理),匹配标签之间的任何文本,最后匹配关闭标签。

它创建了4个捕获组:

  1. 标签名称
  2. 属性字符串
  3. 标签内容
  4. 关闭标签

正如您在此演示中所看到的,如果没有闭合标签,我们就无法为其获取捕获组,属性也是如此,但我们始终可以为标签内容获取一个捕获组。这通常可能会成为问题(因为我们不能假设捕获的特征将在同一组中),但在这种情况下并不是这样,因为在冲突情况下,我们既没有属性也没有内容,因此第二个捕获组为空,我们可以假设它表示没有属性,缺少第三个组说明了问题。如果没有要解析的内容,就不会出现错误解析。

现在,要解析属性,我们可以简单地使用以下方法:

([a-z]+=\"[^\"]*?\")

此处演示。这样可以准确地获取属性。如果Sublime的脚本让您走得更远,那么它肯定会允许您进行进一步处理。当然,您也可以始终使用类似于以下内容的东西:

(([a-z]+)=\"([^\"]*?)\")

这将为整个属性及其名称和值分别提供捕获组。

使用这种方法,您应该能够在2-3次遍历中很好地解析标记,并将内容发送到任何您想要的高亮器进行高亮(或者只是以您想要的任何花哨方式将其作为纯文本进行高亮)。


0

我对sublimetext或react-jsx不熟悉,但对我来说,这听起来像是一个"Regex是你的工具,而不是你的解决方案"的例子。

使用正则表达式作为此工具的解决方案会是类似于这个JsFiddle的东西(请注意,由于html实体(如&gt;代替>等),正则表达式略有混淆。)

执行实际替换的代码:

blabla.replace(/(&lt;!--(?:[^-]|-(?!-&gt;))*--&gt;)|(&lt;(?:(?!&gt;).)+&gt;)|(\{[^\}]+\})/g, function(m, c, t, a) {
    if (c!=undefined)
        return '<span class="comment">' + c + '</span>';
    if (t!=undefined)
        return '<span class="tag">' + t.replace(/ [a-z_-]+=?/ig, '<span class="attr">$&</span>') + '</span>';
    if (a!=undefined)
        return a.replace(/'[^']+'/g, '<span class="quoted">$&</span>');
});

所以在这里,我首先捕获了遵循这一般模式的不同类型组,适用于具有 accolade-blocks 的 HTML 用例。这些捕获被送到一个函数中,该函数确定我们正在处理哪种类型的捕获,并进一步使用其自己的.replace()语句替换此捕获中的子组。

实际上没有其他可靠的方法来完成这个任务。我无法告诉您如何将其转换为您的环境,但也许这会对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接