替换Javascript中正则表达式匹配的第n个实例

替换Javascript中正则表达式匹配的第n个实例,javascript,regex,Javascript,Regex,我正在尝试编写一个正则表达式函数,该函数将识别和替换字符串中匹配项的单个实例,而不会影响其他实例。例如,我有以下字符串: 12||34||56 12||34&&56 我想用符号替换第二组管道以获得此字符串: 12||34||56 12||34&&56 regex函数需要能够处理x个数量的管道,并允许我替换第n组管道,因此我可以使用相同的函数进行这些替换: 23||45||45||56||67 -> 23&&45||45||56||67

我正在尝试编写一个正则表达式函数,该函数将识别和替换字符串中匹配项的单个实例,而不会影响其他实例。例如,我有以下字符串:

12||34||56
12||34&&56
我想用符号替换第二组管道以获得此字符串:

12||34||56
12||34&&56
regex函数需要能够处理x个数量的管道,并允许我替换第n组管道,因此我可以使用相同的函数进行这些替换:

23||45||45||56||67 -> 23&&45||45||56||67

23||34||98||87 -> 23||34||98&&87

我知道我可以在管道处拆分/替换/合并字符串,我还知道我可以在
/\\\\\\\\\\\\\/
上进行匹配并遍历结果数组,但我想知道是否有可能编写一个表达式来实现这一点。请注意,这是针对Javascript的,因此可以在运行时使用
eval()
生成正则表达式,但不可能使用任何特定于Perl的正则表达式指令。

以下是一些有效的方法:

"23||45||45||56||67".replace(/^((?:[0-9]+\|\|){n})([0-9]+)\|\|/,"$1$2&&")
其中n是小于第n个管道的一个(当然,如果n=0,则不需要第一个子表达式)

如果您想要一个函数来执行此操作:

function pipe_replace(str,n) {
   var RE = new RegExp("^((?:[0-9]+\\|\\|){" + (n-1) + "})([0-9]+)\|\|");
   return str.replace(RE,"$1$2&&");
}
更通用的函数 我遇到了这个问题,尽管标题非常笼统,但公认的答案只处理问题的特定用例

我需要一个更通用的解决方案,所以我写了一个,并想在这里分享

用法 此函数要求您向其传递以下参数:

  • original
    :正在搜索的字符串
  • 模式
    :要搜索的字符串或带有捕获组的RegExp。如果没有捕获组,它将抛出错误。这是因为函数调用原始字符串上的
    split
    ,并且
  • n
    :要查找的顺序事件;例如,如果您想要第二个匹配,请传入
    2
  • replace
    :用以替换匹配项的字符串,或接收匹配项并返回替换字符串的函数
例子 代码 如果这样做,可以对任何字符串调用该方法,如下所示:

String.prototype.replaceNthMatch = function(pattern, n, replace) {
  // Same code as above, replacing "original" with "this"
};
"foo-bar-foo".replaceNthMatch("foo", 2, "baz"); // "foo-bar-baz"
通过测试 以下是此函数通过的Jasmine测试

describe("replaceNthMatch", function() {

  describe("when there is no match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("hello-there", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("hello-there");
    });

  });

  describe("when there is no Nth match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("blah45stuff68hey", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("blah45stuff68hey");
    });

  });

  describe("when the search argument is a RegExp", function() {

    describe("when it has a capture group", function () {

      it("should replace correctly when the match is in the middle", function(){
        var str = replaceNthMatch("this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("this_937_thing_NEW_has_21_numbers");
      });

      it("should replace correctly when the match is at the beginning", function(){
        var str = replaceNthMatch("123_this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("123_this_NEW_thing_38_has_21_numbers");
      });

    });

    describe("when it has no capture group", function() {

      it("should throw an error", function(){
        expect(function(){
          replaceNthMatch("one_1_two_2", /\d+/, 2, 'NEW');
        }).toThrow('RegExp must have a capture group');
      });

    });


  });

  describe("when the search argument is a string", function() {

    it("should should match and replace correctly", function(){
      var str = replaceNthMatch("blah45stuff68hey", 'stuff', 1, 'NEW');
      expect(str).toEqual("blah45NEW68hey");
    });

  });

  describe("when the replacement argument is a function", function() {

    it("should call it on the Nth match and replace with the return value", function(){

      // Look for the second number surrounded by brackets
      var str = replaceNthMatch("foo[1][2]", /(\[\d+\])/, 2, function(val) {

        // Get the number without the [ and ]
        var number = val.slice(1,-1);

        // Add 1
        number = parseInt(number,10) + 1;

        // Re-format and return
        return '[' + number + ']';
      });
      expect(str).toEqual("foo[1][3]");

    });

  });

});
可能在IE7中不起作用
这段代码在IE7中可能会失败,因为该浏览器使用正则表达式错误地拆分字符串,如前所述。[与IE7握手]。我相信这就是解决办法;如果您需要支持IE7,祝您好运。:)

好极了!这应该是Node.js和Bower包。@vaughan-谢谢!如果你愿意的话,可以随意做一个。如果正则表达式包含多对括号怎么办?@principal-ideal-domain有一个很好的观点。我不能用这个,因为我的模式中有括号。我正试图找到一个修改的代码来修复这个问题,但我现在运气好。
describe("replaceNthMatch", function() {

  describe("when there is no match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("hello-there", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("hello-there");
    });

  });

  describe("when there is no Nth match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("blah45stuff68hey", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("blah45stuff68hey");
    });

  });

  describe("when the search argument is a RegExp", function() {

    describe("when it has a capture group", function () {

      it("should replace correctly when the match is in the middle", function(){
        var str = replaceNthMatch("this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("this_937_thing_NEW_has_21_numbers");
      });

      it("should replace correctly when the match is at the beginning", function(){
        var str = replaceNthMatch("123_this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("123_this_NEW_thing_38_has_21_numbers");
      });

    });

    describe("when it has no capture group", function() {

      it("should throw an error", function(){
        expect(function(){
          replaceNthMatch("one_1_two_2", /\d+/, 2, 'NEW');
        }).toThrow('RegExp must have a capture group');
      });

    });


  });

  describe("when the search argument is a string", function() {

    it("should should match and replace correctly", function(){
      var str = replaceNthMatch("blah45stuff68hey", 'stuff', 1, 'NEW');
      expect(str).toEqual("blah45NEW68hey");
    });

  });

  describe("when the replacement argument is a function", function() {

    it("should call it on the Nth match and replace with the return value", function(){

      // Look for the second number surrounded by brackets
      var str = replaceNthMatch("foo[1][2]", /(\[\d+\])/, 2, function(val) {

        // Get the number without the [ and ]
        var number = val.slice(1,-1);

        // Add 1
        number = parseInt(number,10) + 1;

        // Re-format and return
        return '[' + number + ']';
      });
      expect(str).toEqual("foo[1][3]");

    });

  });

});