使用字符串变量动态创建正则表达式

150
说我想要让以下内容可重复使用:
function replace_foo(target, replacement) {
   return target.replace("string_to_replace",replacement);
}

我可能会这样做:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(string_to_replace,replacement);
}

使用字符串字面值很容易。但是如果我想在正则表达式中变得更加复杂怎么办?例如,假设我想替换除string_to_replace之外的所有内容。直觉上,我会试着通过以下方式进行扩展:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(/^string_to_replace/,replacement);
}

这似乎不起作用。我猜想它认为 string_to_replace 是一个字符串字面量,而不是表示字符串的变量。是否可能使用字符串变量动态创建JavaScript正则表达式?如果可能的话,像这样做将会很好:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
6个回答

230

new RegExp(string,flags)这样的语法,其中flags可以是gi。所以

'GODzilla'.replace( new RegExp('god', 'i'), '' )

等同于

zilla

35
使用这种形式时,也要省略 / 正则表达式界定符号。 - cdhowie

117

对于字符串字面量而言,这很容易。

实际上不是这样!这个例子只会替换第一个出现的 string_to_replace。更常见的情况是你需要替换所有出现的,这时你需要将字符串转换为全局 (/.../g) 正则表达式。你可以使用 new RegExp 构造函数从字符串中创建它:

new RegExp(string_to_replace, 'g')

这种方法的问题是,字符串中任何正则表达式特殊字符将以它们的特殊方式而不是正常字符的方式进行处理。您必须使用反斜杠转义它们以解决此问题。不幸的是,没有内置函数可以为您执行此操作,因此以下是一个可供使用的函数:

function escapeRegExp(s) {
    return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

请注意,在replace()中使用正则表达式时,替换字符串也有一个特殊字符$。如果您想在替换文本中使用字面量$,那么这个特殊字符也必须被转义!

function escapeSubstitute(s) {
    return s.replace(/\$/g, '$$$$');
}

(四个$,因为那本身就是一个替换字符串——哎呀!)

现在你可以使用RegExp实现全局字符串替换:

function replace_foo(target, string_to_replace, replacement) {
    var relit= escapeRegExp(string_to_replace);
    var sub= escapeSubstitute(replacement);
    var re= new RegExp(relit, 'g');
    return target.replace(re, sub);
}

真是一件痛苦的事情。不过,如果你只想进行简单的字符串替换而没有使用正则表达式的其他部分,那么有一个更快的方法:

s.split(string_to_replace).join(replacement)

......就是这样。这是一个常见的习语。

如果我想替换除了string_to_replace以外的所有东西,怎么办?

这是什么意思,您想替换所有未参与与字符串匹配的文本?使用 ^ 进行替换肯定不行,因为 ^ 表示字符串开头的标记,而不是否定。 ^ 只在 [] 字符组中起到否定作用。还有负向先行断言 (?!...),但在 JScript 中存在问题,所以通常应该避免使用它。

您可以尝试匹配“直到”字符串的所有内容,并使用函数丢弃匹配字符串之间的任何空白内容:

var re= new RegExp('(.*)($|'+escapeRegExp(string_to_find)+')')
return target.replace(re, function(match) {
    return match[1]===''? match[2] : replacement+match[2];
});

这里,再次使用分割可能会更简单:

var parts= target.split(string_to_match);
for (var i= parts.length; i-->0;)
    if (parts[i]!=='')
        parts[i]= replacement;
return parts.join(string_to_match);

15

如其他人所说,使用 new RegExp(pattern, flags) 来实现此功能。值得注意的是,您将向该构造函数传递字符串字面量,因此每个反斜杠都必须被转义。例如,如果您想使正则表达式匹配一个反斜杠,您需要这样写:new RegExp('\\\\'),而正则表达式文本只需要写成 /\\/。根据您打算如何使用它,您应该小心地在没有足够预处理(转义特殊字符等)用户输入的情况下将其传递给此类函数。否则,您的用户可能会得到一些非常意外的结果。


5
虽然这个答案不是最详细的,但它提到了一些至关重要的细节,这正是我卡了一个小时的地方:需要转义任何特殊序列。例如,我正在搜索以某个词开始的单词,所以我需要的正则表达式是 /\b[term]\B/ ,但在构建它时,我需要调用 new RegExp("\\b"+ term + "\\B")。这个差别虽小,但很重要,而且很难注意到,因为直接将其用作正则表达式确实可以按预期工作。 - Byson

7

3
一个非常简单的解决方案如下所示:
function replace(target, string_to_replace, replacement) {
  return target.split(string_to_replace).join(replacement);
}

完全不需要正则表达式

在现代浏览器上似乎也是最快的 https://jsperf.com/replace-vs-split-join-vs-replaceall


1

我认为我有一个非常好的例子来突出字符串中的文本(它不是根据大小写查找,而是使用大小写突出显示)

function getHighlightedText(basicString, filterString) {

    if ((basicString === "") || (basicString === null) || (filterString === "") || (filterString === null)) return basicString;

    return basicString.replace(new RegExp(filterString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\\\$&'), 'gi'),
        function(match)
            {return "<mark>"+match+"</mark>"});

}

http://jsfiddle.net/cdbzL/1258/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接