2

在许多语言中,可以将正则表达式捕获组分配给一个或多个变量。XQuery 中也是这样吗?到目前为止,我们得到的最好的结果是“由捕获组替换”,但这似乎不是最漂亮的选择。

这就是我们现在所拥有的:

let $text := fn:replace($id, '(.+)(\d+)', '$1');
let $snr := fn:replace($id, '(.+)(\d+)', '$2');

哪个有效。但我希望有这样的东西:

let ($text, $snr) := fn:matches($id, '(.+)(\d+)');

那(或类似的东西)是否存在?

4

2 回答 2

3

Plain XQuery 1.0 不支持返回匹配组。这个缺点已经在提供的 XQuery 函数库中functx:get-matches得到了解决,但是实现起来并不高效。

XQuery 3.0 知道非常强大的功能fn:analyze-string。该函数返回匹配和不匹配的部分,如果匹配组在正则表达式中定义,则也按匹配组拆分。

上面链接的 Marklogic 文档中的一个示例,但该函数来自标准 XPath/XQuery 3.0 函数库,也可用于其他 XQuery 3.0 实现:

fn:analyze-string('Tom Jim John',"((Jim) John)")

=>
<s:analyze-string-result>
  <s:non-match>Tom </s:non-match>
  <s:match>
    <s:group nr="1">
    <s:group nr="2">Jim</s:group>
    John
    </s:group>
  </s:match>
</s:analyze-string-result>

如果您不支持 XQuery 3.0:某些引擎提供类似的实现定义的函数或允许使用 Java 代码等后端函数,请在这种情况下阅读您的 XQuery 引擎的文档。

于 2016-10-24T10:24:38.840 回答
0

如果您知道捕获组中没有出现某个字符,则可以在组之间使用替换该字符,然后在 XQuery 1 中对其进行标记。

例如:

tokenize(replace("abc1234", "(.+)(\d+)", "$1-$2"), "-")

要确保替换删除组之前/之后的所有内容:

tokenize(replace("abc1234", "^.*?(.+?)(\d+).*?$", "$1-$2"), "-")

您可以通过使用 string-join 为任何分隔符创建一个替换模式,如 "$1-$2-$3-$4" 将其推广到一个函数:

declare function local:get-matches($input, $regex, $separator, $groupcount) {
  tokenize(replace($input, concat("^.*?", $regex, ".*?$"), string-join(for $i in 1 to $groupcount return concat("$", $i), $separator)), $separator, "q" )
};
local:get-matches("abc1234", "(.+?)(\d+)", "|", 2)

如果您不想自己指定分隔符,则需要一个函数来查找。每个长于输入字符串的字符串都不能出现在捕获组中,因此您始终可以使用更长的分隔符找到一个:

declare function local:get-matches($input, $regex, $separator) {
  if (contains($input, $separator)) then local:get-matches($input, $regex, concat($separator, $separator))
  else 
    let $groupcount := count(string-to-codepoints($regex)[. = 40])
    return tokenize(replace($input, concat("^.*?", $regex, ".*?$"), string-join(for $i in 1 to $groupcount return concat("$", $i), $separator)), $separator, "q" )
};
declare function local:get-matches($input, $regex) {
  local:get-matches($input, $regex, "|#☎")
};
local:get-matches("abc1234", "(.+?)(\d+)")
于 2016-10-24T13:18:06.603 回答