【问题标题】:RegEx replace returns unexpected result without .*正则表达式替换返回不带 .* 的意外结果
【发布时间】:2015-06-09 18:53:16
【问题描述】:

我正在尝试创建一个执行以下转换的正则表达式:

  1. Apple Orange > AO
  2. Load Module > LM
  3. anApple Orange > O
  4. toLoad Module > M

我找到了一个合适的模式,但发现了一个奇怪的行为。这是我的初步尝试:

/^([A-Z])?[^ ]* ([A-Z])/

用这个表达式在第三个(和第四个)测试用例上运行替换给了我一个令人惊讶的结果:

'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z])/,'$1$2')
> "Orange"

为什么这令人惊讶?好吧,第一组显然不匹配,因为字符串不是以大写字母开头,但是第二组只选择了一个单个大写字母:([A-Z]),而不是后面的所有内容:@987654332 @

令我惊讶的是,在最后一个捕获组之后添加 .* 给了我正确的结果:

'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z]).*/,'$1$2')
> "O"

为什么会发生这种情况超出了我对 JS 和正则表达式的理解。我很高兴知道是什么黑魔法导致单个 [A-Z] 返回多个字符,甚至是一些小写字符。

这是一个可运行的演示:

var testCases = [
      'Apple Orange',
      'Load Module',
      'anApple Orange',
      'toLoad Module'
    ],
    badregex = /^([A-Z])?[^ ]* ([A-Z])/,
    goodregex = /^([A-Z])?[^ ]* ([A-Z]).*/;

document.onreadystatechange = function(n){
  if (document.readyState === "complete"){
      for (var i=0,l=testCases.length; i<l; i++){
        var p = document.createElement('p'),
            testCase = testCases[i];
        p.innerHTML = ""+testCase+" &gt; "+testCase.replace(badregex,'$1$2')
        document.body.appendChild(p);
      }
      document.body.appendChild(document.createElement('hr'));
      for (var i=0,l=testCases.length; i<l; i++){
        var p = document.createElement('p'),
            testCase = testCases[i];
        p.innerHTML = ""+testCase+" &gt; "+testCase.replace(goodregex,'$1$2')
        document.body.appendChild(p);
      }
  }
}

【问题讨论】:

    标签: javascript regex replace capture-group


    【解决方案1】:

    我愿意,

    > "Apple Orange".replace(/(?:^|\s)([A-Z])|./g, "$1")
    'AO'
    

    不要把事情复杂化。只需捕获在空格之后或开头存在的所有大写字符。然后匹配所有剩余的字符。现在用$1 替换所有匹配的字符。请注意,所有匹配的字符都将替换为替换部分中存在的字符。

    DEMO

    为什么?

    'anApple Orange'.replace(/^([A-Z])?[^ ]* ([A-Z])/,'$1$2')
    > "Orange"
    
    • ([A-Z])? 在开头检查可选的大写字母。哪有这回事。所以它会捕获一个空字符串。
    • [^ ]* 匹配零个或多个非空格字符。
    • &lt;space&gt; 匹配空格。
    • ([A-Z]) 仅捕获 Orange 中的第一个字母。
    • 现在将所有匹配的字符替换为$1 -> 空字符串$2 -> O 将为您提供Orange

    【讨论】:

    • @AvinashRaj 只是为了确保:(?:^|\s) 中的 | 仅适用于 ^\s 对吧?非捕获组与管道的左侧无关....对吗?
    • (^|\s) 将捕获行边界的空格或开始。 (?:^|\s) 将匹配空格或行边界的开始。我们在这里使用组而不是 [^\s][\s^] 之类的字符类,因为 char 类中的 ^ 会失去匹配行边界开始的特殊含义。
    • 我的意思是:不是?:^ vs \s,我的意思是,未被捕获的组不属于OR操作。对吧?
    • @RoyiNamir 是的,这两个字符只是意味着它是一个不应该被捕获的组。
    • 是的,你是对的。它只是一个包含^\s 的组或容器
    【解决方案2】:

    您的第一个示例匹配 anApple O$1 是空的,因为 ^([A-Z])? 是可选的且不匹配,而 $2O,因此您将字符串 anApple Orange 中的 anApple O 替换为 O,这将导致 Orange

    【讨论】:

    • 谢谢你的解释,我不知何故混淆了匹配和替换。尽管如此,我还是会接受 Avian 的回答,因为添加了更清洁的方法。
    【解决方案3】:

    您可以使用带有match 的非常简单的正则表达式,而不是使用带有复杂正则表达式的replace,并使用join 来获得所需的输出:

    'anApple Orange'.match(/\b([A-Z])/g).join('')
    //=> O
    
    'Apple Orange'.match(/\b([A-Z])/g).join('')
    //=> AO
    

    【讨论】:

    • 这也是一个很好的解决方案,对我来说唯一的问题是我实际上想在 PHP 而不是 JS 中使用正则表达式,并且在那里进行单个替换比匹配 & 更容易加入。这显然不是你的错,因为我没有在任何地方提到这一点,如果我要采用严格的 JS 特定方法,这将是一个完美的解决方案
    • 即使在 PHP 中也可以使用 implode 函数轻松完成。我会在一分钟内更新。
    • 您不应该将 PHP 代码编辑到答案中,因为问题是针对 JS 的。我可以自己编写 PHP 代码,我只是说替换更适合我跨语言。另外implodejoin函数的常用名称,只是一个别名。
    • 可能是的,但比较replacematch 中使用的正则表达式,我想我会选择更简单的选项。
    猜你喜欢
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    相关资源
    最近更新 更多