如何使用共享反向引用执行多重替换?

3

我需要对数据进行一些转换以实现数据加载的兼容性。嵌套的键值对需要被展开,并且它们的组ID需要在每个子数据的前面加上。

我一直在努力理解这个页面:重复捕获组与捕获重复组,但似乎无法理解。

到目前为止,我的表达式是:

"(?'group'[\w]+)": {\n((\s*"(?'key'[^"]+)": "(?'value'[^"]+)"(?:,\n)?)+)\n},?

工作示例:https://regex101.com/r/Wobej7/1

我知道使用一个或多个中间步骤会简化这个过程,但现在我想知道是否可能。

源数据示例:

"g1": {
  "k1": "v1",
  "k2": "v2",
  "k3": "v3"
},
"g2": {
  "k4": "v4",
  "k5": "v5",
  "k6": "v6"
},
"g3": {
  "k7": "v7",
  "k8": "v8",
  "k9": "v9"
}

期望的转变:

{"g1","k1","v1"},
{"g1","k2","v2"},
{"g1","k3","v3"},
{"g2","k4","v4"},
{"g2","k5","v5"},
{"g2","k6","v6"},
{"g3","k7","v7"},
{"g3","k8","v8"},
{"g3","k9","v9"}

你在哪里使用正则表达式?如果在Notepad++中,你可能会使用^("(\w+)":\h*{\h*)(?:\R\h+"(\w+)":\h*"(\w+)",?|\s*\}(?:,\R)?)并替换为(?{3}\{"$2","$3","$4"\},\n$1:),但你需要点击Replace all多次。 - Wiktor Stribiżew
我一直在使用Sublime Text。我在N++中测试了你的解决方案,虽然它可以解决最终的问题,但一次只能捕获一个子元素。我在Stack Overflow上发布这个问题的原因是想看看是否有人可以帮助我理解重复嵌套捕获组,谢谢! - rumpled
据我所知,这不可能在一个步骤中完成。至少你必须使用两个正则表达式,这意味着需要多点击一次鼠标。 - revo
这是一个关于.NET正则表达式的想法,可以一步完成。点击“上下文”(Context)查看url,但那可能对你没有帮助(: - bobble bubble
以下答案有效吗?如果没有,我可以改进或删除它,如果它没有帮助的话。 - revo
显示剩余3条评论
1个回答

0

简短概述

步骤1

搜索:

("[^"]+"):\s*{[^}]*},?\K

替换为\1

在线演示

步骤2

搜索:

(?:"[^"]+":\s*{|\G(?!\A))\s*("[^"]+"):\s*((?1))(?=[^}]*},?((?1)))(?|(,)|\s*}(,?).*\R*)

替换为:

{\3,\1,\2}\4\n

演示现场

整体哲学

由于各种原因,这不会是一个单行正则表达式解决方案。最重要的原因是我们既不能存储匹配的一部分以供以后引用,也不能在PCRE中进行无限回溯。但幸运的是,大多数类似的问题可以分两步完成。

第一步应该是将组名移动到{...}块的末尾。这样,每次我们想要将匹配转换为单行输出时,都可以有组名。

("[^"]+"):\s*{[^}]*},?\K
  • ( 开始捕获组 #1
    • "[^"]+" 匹配一个组名
  • ) 结束 CG #1
  • :\s*{ 组名应该在一堆其他字符之前
  • [^}]*},? 我们必须继续向上到块的结尾
  • \K 抛弃到目前为止匹配的所有内容

我们将组名保存在第一个捕获组中,并用其替换整个匹配:

\1

现在有一个像这样的代码块:

"g1": {
  .
  .
  .
},

看起来像这样:

"g1": {
  .
  .
  .
},"g1"

下一步是匹配每个块的键值对,同时在块末尾捕获最近添加的组名。
(?:"[^"]+":\s*{|\G(?!\A))\s*("[^"]+"):\s*((?1))(?=[^}]*},?((?1)))(?|(,)|\s*}(,?).*\R*)
  • (?: 开始一个非捕获组
    • "[^"]+" 尝试匹配一个组名
    • :\s*{ 一堆其他字符后应该跟着一个组名
    • | 或者
    • \G(?!\A) 从上次匹配继续
  • ) 非捕获组结束
  • \s*("[^"]+"):\s*((?1)) 然后尝试匹配并捕获一个键值对
  • (?=[^}]*},?((?1))) 同时在块末尾匹配并捕获组名
  • (?|(,)|\s*}(,?).*\R*) 匹配剩余的逗号、括号或换行符等字符

这样,在每次正则表达式引擎成功尝试中,我们都有四个被捕获的数据,它们的顺序是键:

{\3,\1,\2}\4\n
  • \3 组名(在块的末尾添加的那个)
  • \1
  • \2
  • \4 逗号(可能有,也可能没有)

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