0

我有一个 XML 有效负载,我正在尝试形成一个正则表达式模式以用于全局搜索和替换。XML 有效负载如下所示:

<NODE1>A1</NODE1>
<NODE2>B1  B2 B3 B4 </NODE2>
<NODE3>C1</NODE3>

我希望提取NODE2标签之间的文本以单独捕获“B1”、“B2”、“B3”和“B4”。每个“单词”之间的空白数量各不相同。

我可以很容易地捕获整个文本((?<=NODE2>)(.*)(?=<\/NODE2))或第一个“单词”((?<=NODE2>)(\S+).*(?=<\/NODE2)),但似乎找不到在标签之间单独捕获每个单词的工作示例。非常感谢任何帮助。

编辑:

  • 除了空白变体之外,XML 有效负载是根据示例并由单个进程一致生成的。节点值中没有节点属性或尖括号。
  • 这是一次性用例,我有一系列用于测试的有效负载,需要更新为新格式。整个有效载荷很难以新格式重新生成。
  • 我正在使用 VSCode 进行全局搜索和替换。它能够轻松解决我必须做的所有其他更新,这是最后一个,我认为在这种情况下编写解决方案是不必要的 - 正确的模式将解决这个问题。
4

3 回答 3

1

注意正则表达式和 XML 解析

使用正则表达式捕获 XML 中的数据总是有点危险。通常,这些<NODE2>标签可以是书面的< node2 >,或者<NODE2 id="3425">是有效的 XML。具有正向前瞻和后向的正则表达式将不起作用,因为它们通常必须是固定长度。

如果您的 XML 可以安全更改,那么您可以使用它:

/(?<=NODE2>)\s*(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s*(?=<\/NODE2)/gi

你可以在这里测试它:https ://regex101.com/r/Bsul9m/2

我添加了i不区分大小写的标志,以防 XML 包含<node2>而不是<NODE2>.

你想捕捉单词,所以我使用了这个\w+模式。但这不包括特殊字符。因此,您可以将其替换为其他内容,例如\p{L}匹配任何语言的任何字母,并使用\dor添加数字[0-9]

这是为了以最少的更改来回答您的问题。

现在,一个更好的正则表达式可以在不使用解析器的情况下处理 XML

这一次,我不会使用您的前瞻和后视模式,因为我们捕获更多并不重要,因为无论如何我们都会捕获您的 4 个项目。

我们希望能够匹配这些标签:

  • 不区分大小写:<node2>,<Node2><NODE2>.
  • 可选空格:< NODE2>, < node2 >...< / node2>.
  • 可选属性:<node2 attribute="value" id="something">.
  • 可选命名空间:<my-namespace:node2 id="123">

这导致正则表达式更加灵活:

/<\s*((?:[\w-]+:)?)node2\b[^>]*>\s*(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s*<\s*\/\s*\1node2\s*>/gi

你可以在这里测试它:https ://regex101.com/r/j6Bcbs/1

这个的优点是它在 XML 结构上会更灵活一些。它适用于以下内容:

<NODE1>A1</NODE1>
<NODE2 id="1234" title="Second node">B1  B2 B3 B4 </ NODE2 >
< node2>A1 B2 C3 ZZZ4</node2>
<namespace:node2>A1  A2 A3  A4</namespace:node2>
< other-namespace:node2 attr = "value"> W X Y Z</other-namespace:node2>
<NODE3>C1</NODE3>

您将在捕获的组 2 到 5 中拥有 4 个项目。这是因为命名空间是在第一个组中捕获的,因为它在结束标记中必须相同。

解释:

  • <\s*匹配开始标签和可选空格。
  • ((?:[\w-]+:)?)是第一个获得可选命名空间的捕获组(它不是 100% 有效,但在大多数情况下都可以使用)。所以这将捕获namespace:other-namespace:在上面的示例中。
  • node2\b[^>]*>node2匹配一个单词结尾,然后是零个或多个不是结束标记的字符,然后是结束标记。
  • \s*(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s*是您想要获取的 4 个项目的 4 个捕获组(索引 2 到 5)。它们可以被空格包围,并且必须用空格分隔。
  • <\s*\/\s*\1node2\s*>是带有对捕获组 1 的反向引用的结束标记,该捕获组是可选的命名空间。
于 2021-08-31T07:41:27.783 回答
1

如果支持锚并且\G不能在两者之间,您可以使用捕获组<>

(?:<NODE2>|\G(?!^))\s*(\S+)(?=[^<>]*</NODE2>)

模式匹配:

  • (?:非捕获组
    • <NODE2>从字面上匹配
    • |或者
    • \G(?!^)在上一场比赛结束时断言位置,而不是在开始时
  • )关闭非捕获组
  • \s*匹配可选的空白字符
  • (\S+)捕获组 1,匹配 1+ 非空白字符
  • (?=[^<>]*</NODE2>)正向前瞻,断言右边没有出现<>,然后匹配</NODE2>

正则表达式演示

更灵活的变化:

(?:<[^>]*\bNODE2\b[^>]*>|\G(?!^))\s*([^<>\s]+)(?=[^>]*</[^>]*\bNODE2\s*>)

正则表达式演示

于 2021-08-31T08:47:56.427 回答
1

不要尝试使用正则表达式处理 XML。原因请参见此处的示例:

为什么不能使用正则表达式来解析 HTML/XML:通俗易懂的正式解释

这个问题在 XPath 中很容易解决。使用当前版本(3.1)很简单//NODE2 ! tokenize(.)

于 2021-08-31T13:39:16.303 回答