用Java替换正则表达式匹配的第n个实例

用Java替换正则表达式匹配的第n个实例

Replacing the nth instance of a regex match in Javascript

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

1
12||34||56

我想用&符替换第二组管道以获取此字符串:

1
12||34&&56

regex函数需要能够处理x数量的管道,并允许我替换第n组管道,因此我可以使用相同的函数进行以下替换:

1
2
3
23||45||45||56||67 -> 23&&45||45||56||67

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

我知道我可以在管道上拆分/替换/合并字符串,而且我也可以在/\\|\\|/上进行匹配并遍历结果数组,但是我很想知道是否可以编写一个 表达式可以做到这一点。 请注意,这将是针对Javascript的,因此可以在运行时使用eval()生成正则表达式,但是无法使用任何Perl特定的正则表达式指令。


更通用的功能

我遇到了这个问题,尽管标题很笼统,但可接受的答案仅处理该问题的特定用例。

我需要一个通用的解决方案,所以我写了一个,并认为我会在这里分享。

用法

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

  • original:您要搜索的字符串
  • pattern:要搜索的字符串或带有捕获组的RegExp。没有捕获组,它将引发错误。这是因为该函数在原始字符串上调用split,并且仅当提供的RegExp包含捕获组时,结果数组才会包含匹配项。
  • n:要查找的顺序出现;例如,如果您想要第二场比赛,请传入2
  • replace:要么是替换匹配的字符串,要么是将接受匹配并返回替换字符串的函数。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Pipe examples like the OP's
replaceNthMatch("12||34||56", /(\\|\\|)/, 2, '&&') //"12||34&&56"
replaceNthMatch("23||45||45||56||67", /(\\|\\|)/, 1, '&&') //"23&&45||45||56||67"

// Replace groups of digits
replaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 3, 'NEW') //"foo-1-bar-23-stuff-NEW"

// Search value can be a string
replaceNthMatch("foo-stuff-foo-stuff-foo","foo", 2, 'bar') //"foo-stuff-bar-stuff-foo"

// No change if there is no match for the search
replaceNthMatch("hello-world","goodbye", 2,"adios") //"hello-world"

// No change if there is no Nth match for the search
replaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 6, 'NEW') //"foo-1-bar-23-stuff-45"

// Passing in a function to make the replacement
replaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 2, function(val){
  //increment the given value
  return parseInt(val, 10) + 1;
}); //"foo-1-bar-24-stuff-45"

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  var replaceNthMatch = function (original, pattern, n, replace) {
    var parts, tempParts;

    if (pattern.constructor === RegExp) {

      // If there's no match, bail
      if (original.search(pattern) === -1) {
        return original;
      }

      // Every other item should be a matched capture group;
      // between will be non-matching portions of the substring
      parts = original.split(pattern);

      // If there was a capture group, index 1 will be
      // an item that matches the RegExp
      if (parts[1].search(pattern) !== 0) {
        throw {name:"ArgumentError", message:"RegExp must have a capture group"};
      }
    } else if (pattern.constructor === String) {
      parts = original.split(pattern);
      // Need every other item to be the matched string
      tempParts = [];

      for (var i=0; i < parts.length; i++) {
        tempParts.push(parts[i]);

        // Insert between, but don't tack one onto the end
        if (i < parts.length - 1) {
          tempParts.push(pattern);
        }
      }
      parts = tempParts;
    }  else {
      throw {name:"ArgumentError", message:"Must provide either a RegExp or String"};
    }

    // Parens are unnecessary, but explicit. :)
    indexOfNthMatch = (n * 2) - 1;

  if (parts[indexOfNthMatch] === undefined) {
    // There IS no Nth match
    return original;
  }

  if (typeof(replace) ==="function") {
    // Call it. After this, we don't need it anymore.
    replace = replace(parts[indexOfNthMatch]);
  }

  // Update our parts array with the new value
  parts[indexOfNthMatch] = replace;

  // Put it back together and return
  return parts.join('');

  }

另一种定义方式

该函数最不吸引人的部分是它需要4个参数。通过将其作为方法添加到String原型,可以简化为仅需要3个参数,如下所示:

1
2
3
String.prototype.replaceNthMatch = function(pattern, n, replace) {
  // Same code as above, replacing"original" with"this"
};

如果这样做,则可以在任何字符串上调用该方法,如下所示:

1
"foo-bar-foo".replaceNthMatch("foo", 2,"baz"); //"foo-bar-baz"

通过考试

以下是此功能通过的Jasmine测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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,祝您好运。 :)


这是可行的:

1
"23||45||45||56||67".replace(/^((?:[0-9]+\\|\\|){n})([0-9]+)\\|\\|/,"$1$2&&")

其中n是小于第n个管道的那个(当然,如果n = 0,则不需要该第一个子表达式)

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

1
2
3
4
function pipe_replace(str,n) {
   var RE = new RegExp("^((?:[0-9]+\\\\|\\\\|){" + (n-1) +"})([0-9]+)\\|\\|");
   return str.replace(RE,"$1$2&&");
}

1
2
3
4
5
6
7
8
9
10
11
12
function pipe_replace(str,n) {
    m = 0;
    return str.replace(/\\|\\|/g, function (x) {
        //was n++ should have been m++
        m++;
        if (n==m) {
            return"&&";
        } else {
            return x;
        }
    });
}


推荐阅读

    linux命令替换后门?

    linux命令替换后门?,系统,数据,工具,平台,工作,检测,最新,管理系统,通信,商

    linux下替换文件命令?

    linux下替换文件命令?,文件,一致,评论,名称,标的,资料,工作,命令,字符串,内

    ab替换命令linux?

    ab替换命令linux?,名称,网络,地址,标准,命令,文件,信息,第一,数据,字符串,vim

    linux命令查找字符串?

    linux命令查找字符串?,工具,信息,命令,字符串,系统,工作,文件,范本,样式,文

    linux命令替换怎么用?

    linux命令替换怎么用?,数据,字符串,命令,系统,文件,批量,内容,方法,字符集,

    linux下s替换命令?

    linux下s替换命令?,系统,命令,状态,工作,数据,首次,字符串,文件,内容,模式,li

    替换删除linux命令?

    替换删除linux命令?,系统,不了,名称,档案,文件,命令,文件夹,目录,数据,字符

    linux查找和替换命令?

    linux查找和替换命令?,工作,系统,数据,字符串,文件,命令,实时,管理,首次,信

    linux字符替换命令?

    linux字符替换命令?,系统,数据,软件,字符串,文件,命令,实时,首次,位置,工作,l

    linux命令正则表达式?

    linux命令正则表达式?,工作,环境,基础,网络,单位,名称,平台,信息,正规,管理,L

    linux查询函数命令?

    linux查询函数命令?,系统,信息,名称,标准,函数,百度,代码,名字,最新,实时,Lin

    linux命令替换一列?

    linux命令替换一列?,地址,内容,标的,重组,名称,文件,命令,字符串,方法,批量,l

    linux替换命令se?

    linux替换命令se?,地址,系统,工作,状态,命令,标准,工具,服务,信息,文件,Linux

    linux替换命令后门?

    linux替换命令后门?,系统,设备,标准,电脑,软件,异常,网站,网络,名称,设计,如

    linux替换空格命令?

    linux替换空格命令?,电脑,名字,软件,命令,空格,文件,数字,灵活,系统,名称,lin

    linux中vi命令替换?

    linux中vi命令替换?,状态,位置,首开,模式,第一,管理,工作,命令,编辑,文件,在l

    linux命令替换是什么?

    linux命令替换是什么?,系统,工作,数据,情况,文件,字符串,命令,管理,信息,资

    linux如何识别命令?

    linux如何识别命令?,系统,名称,工作,工具,地址,信息,命令,服务,情况,代码,lin

    linux替换字符命令?

    linux替换字符命令?,系统,资料,首次,命令,字符串,表示,内容,字符,批量,文件,L

    linux的替换文件命令?

    linux的替换文件命令?,系统,工作,实时,信息,数据,简介,流程,命令,文件,内容,L