变量顺序正则表达式语法

时间:2009-03-31 01:32:20

标签: html regex html-parsing

有没有办法表明两个或更多的正则表达式短语可以按任何顺序出现?例如,XML属性可以按任何顺序编写。假设我有以下XML:

<a href="home.php" class="link" title="Home">Home</a>
<a href="home.php" title="Home" class="link">Home</a>

我如何编写一个匹配类和标题的匹配项并适用于这两种情况?我主要是寻找允许我检查任何顺序的语法,而不仅仅是匹配类和标题,因为我能做到这一点。有什么方法除了包括两个组合并用'|'连接它们?

编辑:我的偏好是在单个正则表达式中进行,因为我以编程方式构建它并对其进行单元测试。

7 个答案:

答案 0 :(得分:8)

不,我相信使用单个RE进行此操作的最佳方式与您所描述的完全一样。不幸的是,当你的XML有5个不同的属性时,它会变得非常混乱,给你一个个不同的RE来检查。

另一方面,我根本不会使用RE,因为它们不是编程语言。使用XML处理库的旧式方法有什么问题?

如果您需要使用RE,这个答案可能无济于事,但我相信使用合适的工具。

答案 1 :(得分:5)

你考虑过xpath吗? (属性顺序无关紧要)

//a[@class and @title]

将两个<a>节点选为有效匹配。唯一需要注意的是输入必须是xhtml(格式良好的xml)。

答案 2 :(得分:4)

您可以为每个属性创建一个预测,并将它们插入到整个标记的正则表达式中。例如,标签的正则表达式可以是

<a\b[^<>]*>

如果你在XML上使用它,你可能需要更精细的东西。这个基础正则表达式本身将匹配具有零个或多个属性的标记。然后为要匹配的每个属性添加一个lookhead:

(?=[^<>]*\s+class="link")
(?=[^<>]*\s+title="Home")

[^<>]*允许它向前扫描属性,但不会让它超出结束尖括号。在前瞻中匹配前导空格有两个目的:它比在基础正则表达式中匹配它更灵活,并且它确保我们匹配整个属性名称。结合它们我们得到:

<a\b(?=[^<>]*\s+class="link")(?=[^<>]*\s+title="Home")[^<>]+>[^<>]+</a>

当然,为了清楚起见,我做了一些简化的假设。我不允许在等号周围的空格,单引号或属性值周围没有引号,或属性值中的尖括号(我听说是合法的,但我从未见过它)。堵塞这些泄漏(如果你需要)将使正则表达式更加丑陋,但不需要更改基本结构。

答案 3 :(得分:2)

您可以使用命名组从标记中提取属性。运行正则表达式,然后遍历执行所需测试的组。

像这样(未经测试,使用.net正则表达式语法,\ w表示字符,\ s表示空格):

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ />

答案 4 :(得分:1)

最简单的方法是编写一个正则表达式来获取<a .... >部分,然后再编写两个正则表达式来提取类和标题。虽然你可以用一个正则表达式来完成它,但它会非常复杂,并且可能更容易出错。

使用单个正则表达式,您需要类似

的内容
<a[^>]*((class="([^"]*)")|(title="([^"]*)"))?((title="([^"]*)")|(class="([^"]*)"))?[^>]*>

这只是第一手猜测而不检查它是否有效。分裂和克服问题要容易得多。

答案 5 :(得分:0)

第一个临时解决方案可能是执行以下操作。

((class|title)="[^"]*?" *)+

这远非完美,因为它允许每个属性不止一次出现。我可以想象这可以用断言来解决。但是如果你只想提取属性,这可能已经足够了。

答案 6 :(得分:0)

如果要匹配一组元素的排列,可以使用反向引用和零宽度的组合 负向前匹配。

假设您要匹配以下六行中的任何一行:

123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-def-789-abc-0AB

您可以使用以下正则表达式执行此操作:

/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/

后面引用(\1\2),让您参考之前的匹配,以及零 宽度向前匹配((?!...))允许你否定位置匹配,如果是,则表示不匹配 在这个位置包含匹配。结合这两者可确保您的匹配是合法的排列 给定元素,每种可能性只发生一次。

所以,例如,在ruby中:

input = <<LINES
123-abc-456-abc-789-abc-0AB
123-abc-456-abc-789-def-0AB
123-abc-456-abc-789-ghi-0AB
123-abc-456-def-789-abc-0AB
123-abc-456-def-789-def-0AB
123-abc-456-def-789-ghi-0AB
123-abc-456-ghi-789-abc-0AB
123-abc-456-ghi-789-def-0AB
123-abc-456-ghi-789-ghi-0AB
123-def-456-abc-789-abc-0AB
123-def-456-abc-789-def-0AB
123-def-456-abc-789-ghi-0AB
123-def-456-def-789-abc-0AB
123-def-456-def-789-def-0AB
123-def-456-def-789-ghi-0AB
123-def-456-ghi-789-abc-0AB
123-def-456-ghi-789-def-0AB
123-def-456-ghi-789-ghi-0AB
123-ghi-456-abc-789-abc-0AB
123-ghi-456-abc-789-def-0AB
123-ghi-456-abc-789-ghi-0AB
123-ghi-456-def-789-abc-0AB
123-ghi-456-def-789-def-0AB
123-ghi-456-def-789-ghi-0AB
123-ghi-456-ghi-789-abc-0AB
123-ghi-456-ghi-789-def-0AB
123-ghi-456-ghi-789-ghi-0AB
LINES

# outputs only the permutations
puts input.grep(/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/)

对于五个元素的排列,它将是:

/1-(abc|def|ghi|jkl|mno)-
 2-(?!\1)(abc|def|ghi|jkl|mno)-
 3-(?!\1|\2)(abc|def|ghi|jkl|mno)-
 4-(?!\1|\2|\3)(abc|def|ghi|jkl|mno)-
 5-(?!\1|\2|\3|\4)(abc|def|ghi|jkl|mno)-6/x

对于您的示例,正​​则表达式将是

/<a href="home.php" (class="link"|title="Home") (?!\1)(class="link"|title="Home")>Home<\/a>/