我怎么知道字符串中有多少匹配项被替换了?
How can I know how many matches get replaced in a string?
假设我有一个如下所示的函数:
function countReplacements ( string, search, replacement ) {
string.replace ( search, replacement );
}
了解字符串中有多少匹配项被替换的最干净、最可靠的方法是什么?
我想到了以下可能的解决方案:
用代理包装 replacement
值,每次访问其代理值时都会记录。不过,这不能转换为旧版本的 JS。
重新实现 String.prototype.replace
中使用的算法,以便每次替换某些内容时都会记录下来。这根本不是很干净。
如果 search
是字符串或非全局正则表达式,我可以检查是否 string
includes/matches 它。但是如果 search
是一个全局正则表达式,当 JS 支持后视时我不确定这是否有效,也许所有匹配都在实际替换它们之前计算?如果不是这种情况,任何替换都可能导致以下回顾不再匹配,或者现在匹配原始字符串中不匹配的内容。
您认为解决问题的最佳方法是什么?
对于替换为字符串的普通情况,对于 .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
:使用提供给 .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, '_' ));
一个更懒惰但更容易实现的版本就是调用 match
并检查结果的 length
,尽管这需要使用正则表达式 [=49= 遍历字符串]两次:
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, '_' ));
If search is a string or a non-global regex I can check if string includes/matches it. But if search is a global regex, when JS will have support for lookbehinds I'm not sure this will work, maybe all matches are computed before actually replacing them? If this isn't the case any replacement may cause the following lookbehinds to no longer match, or to now match things that it wouldn't have matched in the original string.
这不是问题 - 除了 .replace
之外,使用 .match
获取计数的唯一问题是它需要遍历字符串两次。字符串的替换全部一次计算,其中环视正在查看 原始字符串 。然后,一旦找到所有匹配项并计算出替换项,每个匹配的子字符串将被替换为它的替换项。
假设我有一个如下所示的函数:
function countReplacements ( string, search, replacement ) {
string.replace ( search, replacement );
}
了解字符串中有多少匹配项被替换的最干净、最可靠的方法是什么?
我想到了以下可能的解决方案:
用代理包装
replacement
值,每次访问其代理值时都会记录。不过,这不能转换为旧版本的 JS。重新实现
String.prototype.replace
中使用的算法,以便每次替换某些内容时都会记录下来。这根本不是很干净。如果
search
是字符串或非全局正则表达式,我可以检查是否string
includes/matches 它。但是如果search
是一个全局正则表达式,当 JS 支持后视时我不确定这是否有效,也许所有匹配都在实际替换它们之前计算?如果不是这种情况,任何替换都可能导致以下回顾不再匹配,或者现在匹配原始字符串中不匹配的内容。
您认为解决问题的最佳方法是什么?
对于替换为字符串的普通情况,对于 .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
:使用提供给 .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, '_' ));
一个更懒惰但更容易实现的版本就是调用 match
并检查结果的 length
,尽管这需要使用正则表达式 [=49= 遍历字符串]两次:
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, '_' ));
If search is a string or a non-global regex I can check if string includes/matches it. But if search is a global regex, when JS will have support for lookbehinds I'm not sure this will work, maybe all matches are computed before actually replacing them? If this isn't the case any replacement may cause the following lookbehinds to no longer match, or to now match things that it wouldn't have matched in the original string.
这不是问题 - 除了 .replace
之外,使用 .match
获取计数的唯一问题是它需要遍历字符串两次。字符串的替换全部一次计算,其中环视正在查看 原始字符串 。然后,一旦找到所有匹配项并计算出替换项,每个匹配的子字符串将被替换为它的替换项。