6

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

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

我将如何编写一个检查类和标题并适用于这两种情况的匹配项?我主要是在寻找允许我以任何顺序检查的语法,而不仅仅是匹配类和标题,因为我可以做到这一点。除了包含两个组合并用“|”连接它们之外,还有什么办法吗?

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

4

7 回答 7

8

不,我相信使用单个 RE 的最佳方法正是您所描述的。不幸的是,当你的 XML 可以有 5 个不同的属性时,它会变得非常混乱,给你大量不同的 RE 来检查。

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

如果您需要使用 RE,这个答案可能不会有太大帮助,但我相信使用正确的工具来完成这项工作。

于 2009-03-31T01:40:41.220 回答
5

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

//a[@class and @title]

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

于 2009-03-31T13:44:25.287 回答
4

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

<a\b[^<>]*>

如果您在 XML 上使用它,您可能需要更详细的内容。就其本身而言,此基本正则表达式将匹配具有零个或多个属性的标签。然后为要匹配的每个属性添加一个外观:

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

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

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

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

于 2009-03-31T06:22:36.880 回答
2

您可以使用命名组将属性从标签中提取出来。运行正则表达式,然后循环执行您需要的任何测试的组。

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

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ />
于 2009-03-31T01:48:04.947 回答
1

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

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

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

这只是第一手猜测,没有检查它是否有效。分而治之要容易得多。

于 2009-03-31T01:35:49.267 回答
0

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

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

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

于 2009-03-31T01:38:33.847 回答
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>/
于 2009-03-31T02:35:39.903 回答