【问题标题】:JavaScript: Finding successive matches with .exec()JavaScript:使用 .exec() 查找连续匹配项
【发布时间】:2017-12-27 09:07:36
【问题描述】:

我有一个描述产品的具有无数属性的对象,例如颜色和品牌。我正在寻找一种以段落形式动态生成产品描述的方法(因为 API 不提供),我想出了一种方法,方法是编写在括号中包含“道具”@987654326 的“模板” @。我编写了一个函数来“解析”模板,通过将“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 的工作示例。

我想知道的是为什么我的使用RegExp.prototype.execparse() 函数不能正常工作?

【问题讨论】:

标签: javascript regex string match exec


【解决方案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}}.'));

【讨论】:

    【解决方案2】:

    这是因为您在代码中修改并检查了相同的字符串。 而 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}}.'));

    【讨论】:

      【解决方案3】:

      而不是执行两步替换(找到匹配项,然后用所需值替换第一个匹配项)(这很容易出现问题,例如当将新字符串传递给与旧字符串相同的 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;然后,如果在obj 中有一个名为$1 的键,则该值将代替匹配项放入结果字符串中的正确位置。否则,整个匹配项将被放回(如果您想删除不存在键名的 {{...}},请替换为 '')。

      【讨论】:

      • 我比以前更喜欢这个。我会改用这个——谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-20
      • 1970-01-01
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多