在空格之后使用正则表达式前瞻

前端开发 2026-07-11

我在尝试编写一个简单的脚本来处理JSON5——思路是把它转换成标准JSON,然后让 JSON.parse() 处理剩下的部分。我使用正则表达式来完成转换。

其中有一部分是处理未加引号的键。对于这种情况,我用双引号把未加引号的键括起来。下面给出一个带有示例的片段:

 var sample = `
  unquoted: "this is the first",
  "quoted": "this is the second"
`;
var result = sample.replaceAll(/^\s*(?!")(.*?):/gm, '[$1]');
console.log(result);

//  Result:
//  [unquoted] "this is the first",
//  [ "quoted"] "this is the second"

我知道它还没有写完,因为还没有对第二个双引号的测试。在片段中,我用方括号来代替双引号,以便跟踪发生了什么。

我觉得需要使用负向前瞻来定位未加引号的键。

问题在于,负向前瞻似乎与前面的 \s* 项发生冲突。正则表达式能正确识别出第一个未加引号的键,但却把第二个被引号包围的键前面的空格当作表达式的一部分。如果把该项改成 \s\s,就可以工作,但(a)我不能确定每行开头有多少空格,以及(b)它会错误地识别未加引号的键。

在空格之后使用负向前瞻的技巧是什么?

注: 这个问题并不是关于如何解析JSON5,因此也没有在标签中提及。它关乎正则表达式,以及前瞻在这种情况下的行为。

解决方案

当你的正则表达式 ^\s*(?!")(.*?): 在一个以若干空格开头、随后是一个双引号,并且后面还存在某处冒号的字符串上执行时,情况如下:

  1. 首先,运行引擎用 \s* 捕获所有起始空格
  2. 然后负向前瞻 (?!") 失败,因为下一个输入确实是 ",于是发生回溯,回退一个位置,减少在步骤1 中 \s* 已经匹配的内容。
  3. 现在负向前瞻成功,因为下一个输入字符就是回退到的位置的空格。
  4. 剩余的输入用 (.*?): 读取,直到包括第一个冒号在内的部分。

为了避免匹配到,你可以采取几种做法:

  1. (?!") 改成 (?!["\s]),这样回溯会一直进行,直到不再存在可回溯的情况,此时就不会有匹配。
  2. 不进行负向前瞭瞭前瞻,而是直接 消费 不是双引号的字符(也不是空格),像这样 ^\s*([^"\s].*?):
  3. 鉴于未加引号的键不能包含空格或双引号(以及其他限制),你也可以把选项2 简化为 ^\s*([^"\s:]*):

不过,请注意这并不能覆盖所有可能的键匹配情况:

  1. 键并不一定每个都在单独的一行。例如,{ a: 1, b: 2 } 是有效的JSON5,而你的正则表达式可能不会匹配 b:
  2. 冒号前可能有空白字符,这些并不属于键。
  3. JSON5可能有注释块,字符串字面量也可能跨多行(通过在行末用反斜杠来结束行),因此在行首你可能会匹配到包含冒号的文本,从而错误地将其识别为键。
  4. JSON5也可能存在单引号的键,你也应该将其转换,并确保其中的双引号在尚未被转义时会被正确转义。

总的来说,最好为此使用一个经过充分测试的库。例如,json5.org 就提供了一个这样的库。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章