【问题标题】:Find and Replace all occurrences of a phrase in a json string using capturing groups使用捕获组查找和替换 json 字符串中所有出现的短语
【发布时间】:2016-08-22 21:42:39
【问题描述】:

我有一个字符串化的 JSON,如下所示:

...

"message":null,"elementId:["xyz1","l9ie","xyz1"]}}]}], "startIndex":"1", 
"transitionTime":"3","sourceId":"xyz1","isLocked":false,"autoplay":false
,"mutevideo":false,"loopvideo":false,"soundonhover":false,"videoCntrlVisibility":0,
...,"elementId:["dgff","xyz1","jkh90"]}}]}]

...继续。

我需要处理的部分是elementId 键的值。 (第一行的第二个键,最后一个键)。

此键出现在 JSON 字符串中的多个位置。该键的值是一个包含 4 个字符 id 的数组。

我需要用一个新的 id 替换其中一个。

这个想法的核心是这样的:

var elemId = 'xyz1' // for instance
var regex = new RegExp(elemId, 'g');
var newString = jsonString.replace(regex, newRandomId);
jsonString = newString;

这种方法存在几个问题。正则表达式将匹配 JSON 中任何位置的 id。我需要一个仅在 elementId 数组中匹配它的正则表达式;其他地方都没有。

我正在尝试使用捕获组来匹配我需要的事件,但我无法完全破解它。我有:

/.*elementId":\[".*(xyz1).*"\]}}]/

但这与数组中第一次出现的 'xyz1 不匹配。

所以,首先,我需要一个正则表达式,它可以匹配 elementId 中的所有 'xyz1';但没有其他地方。 elementId 结束后的方括号和大括号序列在字符串中的任何位置都不会改变,如果有帮助的话。

其次,即使我有一个有效的捕获组,string.replace 也不会按预期运行。它不是只替换捕获组中的匹配项,而是替换整个匹配项。

所以,我的第二个要求是只替换捕获的组,而不是整个匹配。

需要一段 js 代码,它将在需要的地方替换我的 'xyz1' 并返回以下字符串(假设 newRandomId 是 'abcd'):

"message":null,"elementId:["abcd","l9ie","abcd"]}}]}], "startIndex":"1", 
"transitionTime":"3","sourceId":"xyz1","isLocked":false,"autoplay":false
,"mutevideo":false,"loopvideo":false,"soundonhover":false,"videoCntrlVisibility":0,
...,"elementId:["dgff","abcd","jkh9"]}}]}]

请注意,'sourceId' 的值不受影响。

编辑:我必须使用 JSON。我无法解析它并使用该对象,因为我不知道旧 id 可能在对象中的所有位置,并且多次循环遍历它(对于多个元素)会很耗时

【问题讨论】:

  • 但是...为什么不直接解析 JSON 并处理对象呢?
  • @DenysSéguret:我必须用随机的新 id 替换多个 id。而且我不知道旧身份可能存在的所有地方。 json很大,多次循环遍历整个对象会花费很多时间
  • 这里并非所有的 id 都是 4 个字符长。这正常吗?
  • @DenysSéguret:不。可能是一种类型。我已经编辑过了。它们都是 4 个字符,由数字和大写或小写字母组成。
  • 小心你的测试。它也应该是"elementId":["abcd","l9ie","abcd"] 而不是"elementId:["abcd","l9ie","abcd"]

标签: javascript json regex


【解决方案1】:

假设您不能只解析和更改 JS 对象,您可以使用 2 个正则表达式:一个用于提取数组,一个用于更改内部所需的 id:

var output = input.replace(/("elementId"\s*:\s*\[)((?:".{4}",?)*)(\])/g, function(_,start,content,end){
  return start + content.replace(/"xyz1"/g, '"rand"') + end;
});

参数_startcontentend 是作为正则表达式(documentation here)的结果产生的:

  • _ 是整个匹配的字符串(从"elementId:\[])。我选择这个名称是因为它是您不使用的参数的旧约定
  • start 是第一组 ("elementId:\[)
  • content 是第二个捕获的组,也就是数组的内部部分
  • end id 第三组,]

使用组而不是硬编码返回字符串中的startend 部分有两个目的

  • 避免重复(DRY 原则)
  • 使可变字符串成为可能(例如,在我的正则表达式中,我接受: 之后的可选空格)

var input = document.getElementById("input").innerHTML.trim();
var output = input.replace(/("elementId":\s*\[)((?:".{4}",?)*)(\])/g, function(_,start,content,end){
  return start + content.replace(/"xyz1"/g, '"rand"') + end;
});

document.getElementById("output").innerHTML = output;
Input:
<pre id=input>
"message":null,"elementId":["xyz1","l9ie","xyz1"]}}]}], "startIndex":"1", 
"transitionTime":"3","sourceId":"xyz1","isLocked":false,"autoplay":false
,"mutevideo":false,"loopvideo":false,"soundonhover":false,"videoCntrlVisibility":0,
...,"elementId":["dgff","xyz1","jkh9"]}}]}]
</pre>
Output:
<pre id=output>
</pre>

注意事项:

  • 如果不是在一个数组中重复搜索到的 id,那么在一个正则表达式中完成整个操作会很容易。但是目前的结构可以很容易地同时处理多个要替换的 id。
  • 我使用非捕获组 (?:...) 来整理传递给外部替换回调的参数

【讨论】:

  • 感谢您的回答。但我不太明白答案。此外,在input.replace 之后快速检查正则表达式表明我的 id 没有得到匹配。但我喜欢你使用函数作为替换的第二个参数的想法。
  • @Area51DetectiveFiction 你试过 sn-p 吗?它有效
  • 你是对的。它在 sn-p 上完美运行。出于某种原因,我无法让它在我的代码库中工作。我会弄清楚。谢谢你的帮助。如果你能解释一下替换函数中的参数是什么(_,开始,内容,结束),那对我很有帮助。
  • @Area51DetectiveFiction 我添加了一些解释。现在清楚了吗?
  • 现在好多了。万分感谢。不知道传递正则表达式会产生这些参数。
猜你喜欢
  • 2013-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-01
  • 2015-07-03
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多