【问题标题】:How can I do multiple replace using a shared backreference?如何使用共享反向引用进行多次替换?
【发布时间】:2018-08-19 03:20:26
【问题描述】:

我需要进行一些数据转换以实现数据加载兼容性。嵌套的键:值对需要展平,并将它们的组 id 附加到每个子数据中。

我一直在尝试理解页面 Repeating a Capturing Group vs. Capturing a Repeated Group 但我似乎无法理解它。

到目前为止我的表达:

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

工作样本:https://regex101.com/r/Wobej7/1

我知道使用 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:),但您必须多次单击 全部替换
  • 我一直在 Sublime Text 中使用它。我在 N++ 中测试了您的解决方案,虽然它解决了最终解决方案,但它一次不能捕获多个孩子。我在 Stack Overflow 上发帖的原因真的是想看看是否有人可以帮助我理解重复嵌套的捕获组,但谢谢!
  • 据我所知,这是不可能一步完成的。至少您必须使用两个正则表达式,这意味着再单击一次鼠标。
  • 我不确定我是否可以在 2 个步骤中完成它。需要澄清的一点是,实际应用程序中的组没有偶数个数据,它们都不同于 1-15 k:v 对。
  • @Rumpled 在 SublimeText 中,你仍然可以通过 2 个步骤让它工作。但是,您应该精确格式。输入字符串的真实格式是什么?关于重复捕获组,您无法在文本编辑器中使用它们,并且只能使用几种编程语言使用它们。

标签: regex pcre backreference capture-group


【解决方案1】:

TL;博士

第 1 步

搜索:

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

替换为\1

Live demo

第 2 步

搜索:

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

替换为:

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

Live demo

整体哲学

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

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

("[^"]+"):\s*{[^}]*},?\K
  • ( 捕获组 #1 的开始
    • "[^"]+"匹配组名
  • ) CG #1 结束
  • :\s*{ 组名应该在一堆其他字符之前
  • [^}]*},?我们必须继续前进到区块的末尾
  • \K 扔掉所有匹配到的东西

我们在第一个捕获组中保存了我们的组名,并且必须用它替换整个匹配项:

\1

现在是这样的块:

"g1": {
  .
  .
  .
},

看起来像这样:

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

下一步是匹配每个块的键:值对,除了在块末尾捕获最近添加的组名。

(?:"[^"]+":\s*{|\G(?!\A))\s*("[^"]+"):\s*((?1))(?=[^}]*},?((?1)))(?|(,)|\s*}(,?).*\R*)
  • (?: 非捕获组的开始
    • "[^"]+"尝试匹配组名
    • :\s*{ 组名应该跟在一堆其他字符之后
    • |或者
    • \G(?!\A) 从上一场比赛继续
  • )NCG 结束
  • \s*("[^"]+"):\s*((?1))然后尝试匹配并捕获一个key:value对
  • (?=[^}]*},?((?1)))同时匹配并捕获block末尾的组名
  • (?|(,)|\s*}(,?).*\R*) 匹配剩余字符,例如逗号、大括号或换行符

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-27
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多