javascript - 在字符串中保持匹配的正则表达式,直到没有匹配

时间:2017-08-17 18:44:25

标签: javascript regex

  

TL; DR
  如何在字符串中搜索模式并将其替换为可能导致模式再次出现的内容,而不是循环。

所以我遇到这种情况需要在字符串中搜索某些特定模式,如下所示:

  1. 在整个字符串中查找 i。 '(\-\-)+|(\+)+'然后 ii。 (\+\-)+|(\-\+)+,并将 i。替换为+ ii。-
  2. 再次检查如果字符串不在 i。 ii。中,如果是,则中断,否则,请转到步骤1.
  3. 这就是我尝试过的(字符串是str),问题是它只搜索字符串一次,即:

    var str = '1+-2--++--+4---++--+2';
    str.replace(/(\-\-)+|(\+)+/g,'+').replace(/(\+\-)+|(\-\+)+/g,'-');
    

    第一次替换后的字符串(()只是为了突出显示更改,它们不是字符串的一部分):

    1(+)-2(--)(++)(--)(+)4(--)-(++)(--)(+)2 => 1(+)-2(+)(+)(+)(+)4(+)-(+)(+)(+)2
    

    第二次更换后:

    1(+-)2++++4(+-)+++2 => 1(-)2++++4(-)+++2
    

    就是这样,但我还没有完成,仍然出现++++-+
    我希望它继续这样:

    1-2(++++)4-(+++)2 => 1-2(+)4-(+)2
    1-2(+)4(-+)2 => 1-2(+)4(-)2 => # and the final result 1-2+4-2
    

    我知道可以通过循环来完成,但如果有更好,更简单和更短的方式,如果有人指出它我会很感激。

3 个答案:

答案 0 :(得分:2)

某些分析表明,+-的不间断序列最终会缩减为一个字符(-+)。

这似乎是一种算法,可以将奇数-的序列减少到只有一个-,并在有偶数个时将+减少到一个算法。

但是,如果这确实是所需的行为,则第二个正则表达式不应该指向重复,而是一次只替换一次更改对,即使用/(\+\-)|(\-\+)/g而不是/(\+\-)+|(\-\+)+/g

然后结束-的唯一方法是当序列中有奇数个时,因为第一个替换只能删除偶数个,而第二个替换留下数量他们不变。在所有其他情况下,一个序列的最终结果将是+

所以......那么这个问题可以简化为:

var str = '1+-2--++--+4---++--+2';

var repl = str.replace(/\b(?=[+-])\+*(-\+*-\+*)*\b/g, '+')
              .replace(/\b\+*-[+-]*/g, '-');

console.log(repl);

答案 1 :(得分:2)

注意: 我意识到这个答案基本上重复了trincot的答案,但我已经写了一半,然后在他的答案被张贴之前走开去吃午饭,我不喜欢放弃我的努力浪费了,而且我用不同的方式解释了不同的东西。

最终结果完全基于-计数,有两种情况:

  1. 偶数个- s(包括零),由+取代

    \+*(?:-\+*-\+*)*
    \+*              Zero or more '+'s
       (?:           Non-capturing group
          -            Exactly one '-'
           \+*         Zero or more '+'s
              -        Exactly one '-'
               \+*     Zero or more '+'s
                  )* Repeat capture group Zero or more times
    
  2. 奇数个- s(相同的模式,至少有一个-),替换为-

    \+*-\+*(?:-\+*-\+*)*
    
  3. 但是,这里有一些陷阱。第一个正则表达式匹配一个空字符串,这不是我们想用+替换的东西。我们可以通过前瞻来解决这个问题,它可以验证被测试的字符串是否与某个整体模式匹配而不消耗字符串。

    (?=[+-]+)
    (?=       Positive lookahead
       [+-]     Either or '+' or '-'
           +    One or more times
            ) Close positive lookahead (rest of regex goes after this)
    

    也没有强制只考虑两个数字之间的整个[+-]+字符串,因此第一个正则表达式可能遇到如下情况:

    1++--+-2
    

    找到匹配的字符串++--+,留下

    1+-2
    

    为此,我们可以使用单词边界\b,或者在正则表达式的末尾捕获数字组以及对替换组中的那些组的反向引用。我们将使用\b,因为它是更简单,优雅和有效的解决方案。

    \b是一个特殊的正则表达式字符,它匹配单词字符\w和非单词字符\W

    之间的零长度“边界”
      
        
    • 字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符。 [Source]
    •   

    var str = '1+--+-2--++--+4---++--+2';
    
    var repl = str.replace(/\b(?=[+-])\+*(-\+*-\+*)*\b/g, '+')
                  .replace(/\b\+*-\+*(?:-\+*-\+*)*\b/g, '-');
    
    console.log(repl);

    <强>快捷方式

    我相信让代码更冗长,模块化和自我记录,但如果您的代码的字节数对您更重要,那么第二个正则表达式可以简单地替换为

    \b[+-]{2,}\b
    

    因为在运行第一次替换后,由+-组成的大于长度为1的字符串必须是第一个正则表达式不匹配的字符串,因此它们应替换为-

    var str = '1+--+-2--++--+4---++--+2';
    
    var repl = str.replace(/\b(?=[+-])\+*(-\+*-\+*)*\b/g, '+')
                  .replace(/\b[+-]{2,}\b\b/g, '-');
    
    console.log(repl);

答案 2 :(得分:2)

编辑 修复了底片的正则表达式..现在正常工作。

(Fyi - 请注意,即使这是一个替换,它也会使用回调 在好的方面,它仍然是使用双重替换的两倍。)

我将保存这个以供下次使用,因为正则表达式是实心的,
我曾经多次见过这个问题。

这可以通过一次替换调用。

\b(?:(?:\+|--)*(-(?!-)(?:\+|--)*)+|(?:\+|--)+)\b

 \b 
 (?:
      (?: \+ | -- )*                # optional positives
      (                             # (1 start), Neg (agressive)
           -                             # - sign
           (?! - )                       # not followed by a -
           (?: \+ | -- )*                # optional positives
      )+                            # (1 end)
   |                              # or,
      (?:                           # Pos (passive)
           \+
        |  --
      )+
 )
 \b 

var str = ' 1+-2--++--+4---++--+2--6,,,,5--+--+5 ';

console.log(
   str.replace(/\b(?:(?:\+|--)*(-(?!-)(?:\+|--)*)+|(?:\+|--)+)\b/g,
          function(m, g1) {return g1 ? '-' : '+';})
 );

输出1-2+4-2+6,,,,5+5