JavaScript:使用.exec()查找连续匹配项

问题描述 投票:0回答:3

我有一个具有无数属性的对象,例如颜色和品牌,它描述了一个产品。我正在寻找一种以段落形式动态生成产品描述的方法(因为API没有提供),我想出了一种方法,通过编写带有“props”的括号{{}}包围的“模板”。我编写了一个函数来解析模板,方法是在字符串中注入对象属性,方法是将“props”替换为键的值。

例如:

对象:{color: 'white'}

模板:"The bowl is {{color}}."

结果:"The bowl is white."

出于某种原因,我的解析功能不起作用。 {{general_description}}未被解析。

var obj = {
  brand: "Oneida",
  general_description: "Plate",
  material: "China",
  color: "Bone White",
  product_width: "5\""
};

const templatePropRe = /{{(\w*)}}/g;
const parse = (template) => {
  while ((result = templatePropRe.exec(template)) !== null) {
    let match = result[0],
      key = result[1];
    template = template.replace(match, obj[key]);
  }
  return template;
}

console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

我按照示例>查找连续匹配中的MDN docs中提供的示例进行操作。它说我需要首先将正则表达式存储在变量(例如,templatePropRe)中,因为表达式不能处于while循环条件中,否则它将无限循环。但是,如果我这样做,我的问题就解决了。看here ......什么都没破。

我使用String.prototype.match重写了该函数,它按预期工作,但我无法访问捕获所以我需要首先使用stripBrackets剥离括号。请参阅使用match here的工作示例。

我想知道的是为什么我的使用parse()RegExp.prototype.exec功能不能正常工作?

javascript regex string match exec
3个回答
1
投票

从你的正则表达式中删除/g标志。根据documentation,当这个标志出现时,它会更新正则表达式对象的lastIndex属性,该属性指示下一次调用exec()将开始搜索匹配的位置。

var obj = {
  brand: "Oneida",
  general_description: "Plate",
  material: "China",
  color: "Bone White",
  product_width: "5\""
};

const templatePropRe = /{{(\w*)}}/;
const parse = (template) => {
  while ((result = templatePropRe.exec(template)) !== null) {
    let match = result[0],
      key = result[1];
      
    template = template.replace(match, obj[key]);
  }
  
  return template;
}

console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

1
投票

发生这种情况是因为您修改并检查代码中的相同字符串。而regExp在每次执行后都会保存匹配子字符串的索引,您可以更改字符串的长度,而下一次执行的regEx将从您预期的其他点开始。

var obj = {
  brand: "Oneida",
  general_description: "Plate",
  material: "China",
  color: "Bone White",
  product_width: "5\""
};

const templatePropRe = /{{(\w*)}}/g;
const parse = (template) => {
  var resultStr = template;
  while ((result = templatePropRe.exec(template)) !== null) {
    let match = result[0],
      key = result[1];
    resultStr = resultStr.replace(match, obj[key]);
  }
  return resultStr;
}

console.log(parse('This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

1
投票

而不是执行两步替换(找到一个匹配,然后用所需的值替换第一个匹配)(这很容易出现像新的字符串传递给具有旧的已经无效的同一个RegExp时遇到的问题,索引),你可以使用callback method as a replacement argument inside a String#replace method。这样,结果字符串将在每次匹配时动态构建,使代码执行得更快。

请参阅下面的示例修复:

var obj = {
  brand: "Oneida",
  general_description: "Plate",
  material: "China",
  color: "Bone White",
  product_width: "5\""
};
const parse = (template) => {
  return template.replace(/{{(\w*)}}/g, ($0, $1) => obj[$1] ? obj[$1] : $0 );
  // ES5 way:
  // return template.replace(/{{(\w*)}}/g, function($0, $1) {
  //       return obj[$1] ? obj[$1] : $0;
  // });
}

console.log(parse('{{keep}} This {{color}}, {{material}} {{general_description}} supplied by {{brand}} has a width of {{product_width}}.'));

注意,在找到匹配之后,($0, $1) => obj[$1] ? obj[$1] : $0代码执行以下操作:将整个匹配分配给$0变量,将组1值分配给$1;然后,如果在$1中有一个名为obj的键,则该值将被放入生成的字符串中的正确位置而不是匹配。否则,整个比赛被放回(如果你想删除一个不存在的密钥名称的'',则用{{...}}替换)。

© www.soinside.com 2019 - 2024. All rights reserved.