gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 01:15:03 25 4
gpt4 key购买 nike

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

12||34||56

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

12||34&&56

正则表达式函数需要能够处理 x 数量的管道并允许我替换第 n 组管道,因此我可以使用相同的函数来进行这些替换:

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

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

我知道我可以在管道处拆分/替换/连接字符串,而且我也知道我可以匹配 /\|\|/ 并迭代结果数组,但是我很想知道是否可以编写一个可以执行此操作的表达式。请注意,这适用于 Javascript,因此可以使用 eval() 在运行时生成正则表达式,但不可能使用任何特定于 Perl 的正则表达式指令。

最佳答案

更通用的函数

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

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

用法

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

  • original:您要搜索的字符串
  • pattern:要搜索的字符串,或者带有捕获组的正则表达式。如果没有捕获组,它将引发错误。这是因为该函数对原始字符串调用 split,并且 only if the supplied RegExp contains a capture group will the resulting array contain the matches .
  • n:要查找的顺序出现;例如,如果您想要第二场比赛,请传入 2
  • replace:用于替换匹配项的字符串,或者接受匹配项并返回替换字符串的函数。

示例

// 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"

代码

  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 原型(prototype)中,可以将其简化为只需要 3 个参数,如下所示:

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 中可能会失败,因为该浏览器错误地使用正则表达式分割字符串,如所述 here 。 [对 IE7 握拳]。我相信this是解决方案;如果你需要支持IE7,祝你好运。 :)

关于javascript - 替换 Javascript 中正则表达式匹配的第 n 个实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36183/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com