假设我有一个看起来像这样的函数:
function countReplacements ( string, search, replacement ) {
string.replace ( search, replacement );
}
什么是最干净,最可靠的方法来知道将多少匹配替换为字符串?
我想到了以下可能的解决方案:
用代理服务器包装replacement
值,该代理服务器每次访问其代理值时都会记录一次。但是,这不能移植到旧版本的JS。
重新实现String.prototype.replace
中使用的算法,以便每次替换该算法时都会记录此内容。这根本不是很干净。
如果search
是字符串或非全局正则表达式,我可以检查string
是否包含/匹配它。但是,如果search
是全局正则表达式,那么当JS将支持lookbehinds时,我不确定这是否行得通,也许所有匹配都在实际替换它们之前就已经计算了?如果不是这种情况,则任何替换都可能导致以下回溯不再匹配,或者现在匹配原始字符串中不匹配的内容。
您认为什么是解决问题的最佳方法?
答案 0 :(得分:1)
对于替换为字符串的普通情况,对于.replace
的第二个参数,请使用回调函数,而不要使用普通的replacement
字符串,并使回调增量为变量:
function countReplacements(string, search, replacement) {
let count = 0;
const result = string.replace(search, () => {
count++;
return replacement;
});
return { count, result };
}
console.log(countReplacements('foobar', /o/g, 'a'));
对于更复杂的情况,当replacement
是一个函数或包含组引用的字符串时,您将不得不自己重新实现String.prototype.replace
:使用提供给{{1 }}获得完整的比赛和分组:
.replace
更懒惰但更易于实现的版本只是调用function countReplacements(string, search, replacement) {
let count = 0;
const result = string.replace(search, (match, ...groups) => {
count++;
return replacement
.replace(/\$(\d+|&)/g, (_, indicator) => {
if (indicator === '&') return match;
if (/^\d+$/.test(indicator)) return groups[indicator - 1];
// and so on for other `$`s
});
});
return { count, result };
}
console.log(countReplacements ( 'foobar', /(o)/g, '$1_' ));
并检查结果match
,尽管这将需要使用正则表达式两次遍历字符串:
length
如果搜索是字符串或非全局正则表达式,我可以检查字符串是否包含/匹配它。但是,如果搜索是一个全局正则表达式,那么当JS将支持lookbehinds时,我不确定是否可以正常工作,也许所有匹配项都在实际替换它们之前进行了计算?如果不是这种情况,则任何替换都可能导致以下回溯不再匹配,或者现在匹配原始字符串中不匹配的内容。
这不会有问题-除了function countReplacements(string, search, replacement) {
const match = string.match(search);
const count = match ? match.length : 0;
const result = string.replace(search, replacement);
return { count, result };
}
console.log(countReplacements ( 'foobar', /(o)/g, '$1_' ));
之外,使用.match
获取计数的唯一问题是它需要两次遍历字符串。字符串的替换全部一次计算,环顾四周是原始字符串。然后,一旦找到所有匹配项并计算了替换项,则将每个匹配的子字符串替换为其替换项。