Create RegExps on the fly using string variables

2019-01-02 18:08发布

问题:

Say I wanted to make the following re-usable:

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

I might do something like this:

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

With string literals this is easy enough. But what if I want to get a little more tricky with the regex? For example, say I want to replace everything but string_to_replace. Instinctually I would try to extend the above by doing something like:

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

This doesn't seem to work. My guess is that it thinks string_to_replace is a string literal, rather than a variable representing a string. Is it possible to create JavaScript regexes on the fly using string variables? Something like this would be great if at all possible:

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

回答1:

There's new RegExp(string, flags) where flags are g or i. So

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

evaluates to

zilla


回答2:

With string literals this is easy enough.

Not really! The example only replaces the first occurrence of string_to_replace. More commonly you want to replace all occurrences, in which case, you have to convert the string into a global (/.../g) RegExp. You can do this from a string using the new RegExp constructor:

new RegExp(string_to_replace, 'g')

The problem with this is that any regex-special characters in the string literal will behave in their special ways instead of being normal characters. You would have to backslash-escape them to fix that. Unfortunately, there's not a built-in function to do this for you, so here's one you can use:

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

Note also that when you use a RegExp in replace(), the replacement string now has a special character too, $. This must also be escaped if you want to have a literal $ in your replacement text!

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

(Four $s because that is itself a replacement string—argh!)

Now you can implement global string replacement with 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);
}

What a pain. Luckily if all you want to do is a straight string replace with no additional parts of regex, there is a quicker way:

s.split(string_to_replace).join(replacement)

...and that's all. This is a commonly-understood idiom.

say I want to replace everything but string_to_replace

What does that mean, you want to replace all stretches of text not taking part in a match against the string? A replacement with ^ certainly doesn't this, because ^ means a start-of-string token, not a negation. ^ is only a negation in [] character groups. There are also negative lookaheads (?!...), but there are problems with that in JScript so you should generally avoid it.

You might try matching ‘everything up to’ the string, and using a function to discard any empty stretch between matching strings:

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

Here, again, a split might be simpler:

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);


回答3:

As the others have said, use new RegExp(pattern, flags) to do this. It is worth noting that you will be passing string literals into this constructor, so every backslash will have to be escaped. If, for instance you wanted your regex to match a backslash, you would need to say new RegExp('\\\\'), whereas the regex literal would only need to be /\\/. Depending on how you intend to use this, you should be wary of passing user input to such a function without adequate preprocessing (escaping special characters, etc.) Without this, your users may get some very unexpected results.



回答4:

Yes you can.

https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions

function replace_foo(target, string_to_replace, replacement) {
   var regex = new RegExp("^" + string_to_replace);
   return target.replace(regex, replacement);
}


回答5:

I think I have very good example for highlight text in string (it finds not looking at register but highlighted using register)

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/



回答6:

A really simple solution to this is this:

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

No need for Regexes at all

It also seems to be the fastest on modern browsers https://jsperf.com/replace-vs-split-join-vs-replaceall



标签: