ChatGPT解决这个技术问题 Extra ChatGPT

Fastest method to replace all instances of a character in a string [duplicate]

This question already has answers here: How to replace all occurrences of a string in JavaScript (79 answers) Closed 2 years ago. The community reviewed whether to reopen this question 11 months ago and left it closed: Needs details or clarity Add details and clarify the problem by editing this post.

What is the fastest way to replace all instances of a string/character in a string in JavaScript? A while, a for-loop, a regular expression?

A while and a for-loop both would run in O(n) with a simple algorithm. Not really sure what's the time complexity for Javascript regex-engine in this case, but my guess is its optimized enough to run in O(n) for a simple string match.
This seems like micro-optimising to me - did performance profiling show up the string replacement to be the slowest part of your program?
No, I didn't put my script through performance profiling, I was just making sure I'm using the fastest function available.
I've done a JSPerf comparing global regex and a for-loop: jsperf.com/javascript-replace-all. If I've written the tests appropriately, it looks like the answer is "it depends".

M
Martin Tournoij

The easiest would be to use a regular expression with g flag to replace all instances:

str.replace(/foo/g, "bar")

This will replace all occurrences of foo with bar in the string str. If you just have a string, you can convert it to a RegExp object like this:

var pattern = "foobar",
    re = new RegExp(pattern, "g");

str.replace(/foo/g, "bar") caused an error for me. str.replace(/foo/, "bar") works.
Warning: This does not work for strings containing newlines. XRegExp has a replace method that does the trick.
my inner pedant is pointing out that the OP asked for the fastest, not the easiest
I did user.email.replace(/./g,','), and the whole email got replaced with comas in the same number as characters in the email. Puzzled...
@JaredTomaszewski, the full stop (period) character in a regex stands for "any character". To signify an actual full stop, you'd need to precede it with a backslash i.e. user.email.replace(/\./g,',')
C
Colorfully Monochrome

Try this replaceAll: http://dumpsite.com/forum/index.php?topic=4.msg8#msg8

String.prototype.replaceAll = function(str1, str2, ignore) 
{
    return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
} 

It is very fast, and it will work for ALL these conditions that many others fail on:

"x".replaceAll("x", "xyz");
// xyz

"x".replaceAll("", "xyz");
// xyzxxyz

"aA".replaceAll("a", "b", true);
// bb

"Hello???".replaceAll("?", "!");
// Hello!!!

Let me know if you can break it, or you have something better, but make sure it can pass these 4 tests.


This is quite good for replacing strings with unknown content, but his strings are fixed and does not need the complexity of escaping regular expressions. I upped this because I was searching for a replaceAll function.
@jens All I'm saying is a giant CAVEAT EMPTOR. RegExes are super complicated, and a random internet answer that is supposed to solve someone's problem once and for all, but which very, very likely has some lurking bugs, is a poor answer indeed. We don't expect unit tests on answers that are obviously correct—of course not. But we DO expect that answers give the reader a proper understanding of their risks when an inexperienced programmer is likely to be misled into false confidence. My comment helps warn those beginners against trusting this untested, crazy code.
I've started adding unit tests for this function at the bottom of this test suite: github.com/agrothe/alphanumbers/blob/master/test/index.js
The behavior of "x".replaceAll("", "xyz"); seems like a bug to me. If I tried to replace empty string I'd really expect an error.
Since there’s now a standard replaceAll method, this answer overrides it now. Please do monkey patching correctly by checking the existence of a property first!
S
Sani Singh Huttunen
var mystring = 'This is a string';
var newString = mystring.replace(/i/g, "a");

newString now is 'Thas as a strang'


This routine is the fastest on Firefox but very slow on chrome: check my answer: stackoverflow.com/a/57697050/236062
V
Vlada

Also you can try:

string.split('foo').join('bar');

does this handle regular expression ? I guess. But for string to string replacement, this one is my favorite :) Very fast with firefox
@yota Yes. You can use regular expression. "12px (2) bar-456-foo 44".split(/\d/).join("#")
This is perfect for most simple cases. Works great in a nice little function like: function replaceAll( s, f, r ){ return s.split( f ).join( r ); }. Or if you think RegEx is faster: function replaceAll( s, f, r ){ f = RegExp( f, 'gi' ); return s.replace( f, r ); }. Then just do foo = replaceAll( 'aaa', 'a', 'b' );.
The best answer
If your needle is some variable that contains one or more regex reserved characters, then the methods of the other answers can do unexpected replacements. The good thing about this method is that it treats all characters the same way.
s
ssamuel68

You can use the following:

newStr = str.replace(/[^a-z0-9]/gi, '_');

or

newStr = str.replace(/[^a-zA-Z0-9]/g, '_');

This is going to replace all the character that are not letter or numbers to ('_'). Simple change the underscore value for whatever you want to replace it.


Should be .replace(/[a-zA-Z0-9]/g, '_') without the ^
D
DamienS

Just thinking about it from a speed issue I believe the case sensitive example provided in the link above would be by far the fastest solution.

var token = "\r\n";
var newToken = " ";
var oldStr = "This is a test\r\nof the emergency broadcasting\r\nsystem.";
newStr = oldStr.split(token).join(newToken);

newStr would be "This is a test of the emergency broadcast system."


R
Rick Velde

I think the real answer is that it completely depends on what your inputs look like. I created a JsFiddle to try a bunch of these and a couple of my own against various inputs. No matter how I look at the results, I see no clear winner.

RegExp wasn't the fastest in any of the test cases, but it wasn't bad either.

Split/Join approach seems fastest for sparse replacements.

This one I wrote seems fastest for small inputs and dense replacements: function replaceAllOneCharAtATime(inSource, inToReplace, inReplaceWith) { var output=""; var firstReplaceCompareCharacter = inToReplace.charAt(0); var sourceLength = inSource.length; var replaceLengthMinusOne = inToReplace.length - 1; for(var i = 0; i < sourceLength; i++){ var currentCharacter = inSource.charAt(i); var compareIndex = i; var replaceIndex = 0; var sourceCompareCharacter = currentCharacter; var replaceCompareCharacter = firstReplaceCompareCharacter; while(true){ if(sourceCompareCharacter != replaceCompareCharacter){ output += currentCharacter; break; } if(replaceIndex >= replaceLengthMinusOne) { i+=replaceLengthMinusOne; output += inReplaceWith; //was a match break; } compareIndex++; replaceIndex++; if(i >= sourceLength){ // not a match break; } sourceCompareCharacter = inSource.charAt(compareIndex) replaceCompareCharacter = inToReplace.charAt(replaceIndex); } replaceCompareCharacter += currentCharacter; } return output; }


Another consideration to factor in is that the split/join approach is the simplest, shortest, most to-the-point approach, making it the most likely candidate for future browsers to internally optimize it into something several times faster (e.g. instead of creating a new array and a new string, it would instead just search through & copy-n-paste it linearly similar to a regular expression) during JIST compilation.
N
Neel Kamal

Use Regex object like this

var regex = new RegExp('"', 'g'); str = str.replace(regex, '\'');

It will replace all occurrence of " into '.


Where other answers didn't work where the needle is a variable, this one works well.
C
Crozin

What's the fastest I don't know, but I know what's the most readable - that what's shortest and simplest. Even if it's a little bit slower than other solution it's worth to use.

So use:

 "string".replace("a", "b");
 "string".replace(/abc?/g, "def");

And enjoy good code instead of faster (well... 1/100000 sec. is not a difference) and ugly one. ;)


Z
Zibri

I just coded a benchmark and tested the first 3 answers. It seems that for short strings (<500 characters) the third most voted answer is faster than the second most voted one.

For long strings (add ".repeat(300)" to the test string) the faster is answer 1 followed by the second and the third.

Note:

The above is true for browsers using v8 engine (chrome/chromium etc). With firefox (SpiderMonkey engine) the results are totally different Check for yourselves!! Firefox with the third solution seems to be more than 4.5 times faster than Chrome with the first solution... crazy :D

function log(data) { document.getElementById("log").textContent += data + "\n"; } benchmark = (() => { time_function = function(ms, f, num) { var z; var t = new Date().getTime(); for (z = 0; ((new Date().getTime() - t) < ms); z++) f(num); return (z / ms) } // returns how many times the function was run in "ms" milliseconds. function benchmark() { function compare(a, b) { if (a[1] > b[1]) { return -1; } if (a[1] < b[1]) { return 1; } return 0; } // functions function replace1(s) { s.replace(/foo/g, "bar") } String.prototype.replaceAll2 = function(_f, _r){ var o = this.toString(); var r = ''; var s = o; var b = 0; var e = -1; // if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); } while((e=s.indexOf(_f)) > -1) { r += o.substring(b, b+e) + _r; s = s.substring(e+_f.length, s.length); b += e+_f.length; } // Add Leftover if(s.length>0){ r+=o.substring(o.length-s.length, o.length); } // Return New String return r; }; String.prototype.replaceAll = function(str1, str2, ignore) { return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$$$") : str2); } function replace2(s) { s.replaceAll("foo", "bar") } function replace3(s) { s.split('foo').join('bar'); } function replace4(s) { s.replaceAll2("foo", "bar") } funcs = [ [replace1, 0], [replace2, 0], [replace3, 0], [replace4, 0] ]; funcs.forEach((ff) => { console.log("Benchmarking: " + ff[0].name); ff[1] = time_function(2500, ff[0], "foOfoobarBaR barbarfoobarf00".repeat(10)); console.log("Score: " + ff[1]); }) return funcs.sort(compare); } return benchmark; })() log("Starting benchmark...\n"); res = benchmark(); console.log("Winner: " + res[0][0].name + " !!!"); count = 1; res.forEach((r) => { log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")"); }); log("\nWinner code:\n"); log(res[0][0].toString());

The test will run for 10s (+2s) as you click the button.

My results (on the same pc):

Chrome/Linux Ubuntu 64:
1. replace1 score: 100% *winner* (766.18)
2. replace4 score: 99.07% speed of winner. (759.11)
3. replace3 score: 68.36% speed of winner. (523.83)
4. replace2 score: 59.35% speed of winner. (454.78)

Firefox/Linux Ubuntu 64
1. replace3 score: 100% *winner* (3480.1)
2. replace1 score: 13.06% speed of winner. (454.83)
3. replace4 score: 9.4% speed of winner. (327.42)
4. replace2 score: 4.81% speed of winner. (167.46)

Nice mess uh?

Took the liberty of adding more test results

Chrome/Windows 10
1. replace1 score: 100% *winner* (742.49)
2. replace4 score: 85.58% speed of winner. (635.44)
3. replace2 score: 54.42% speed of winner. (404.08)
4. replace3 score: 50.06% speed of winner. (371.73)

Firefox/Windows 10
1. replace3 score: 100% *winner* (2645.18)
2. replace1 score: 30.77% speed of winner. (814.18)
3. replace4 score: 22.3% speed of winner. (589.97)
4. replace2 score: 12.51% speed of winner. (331.13)

Edge/Windows 10
1. replace1 score: 100% *winner* (1251.24)
2. replace2 score: 46.63% speed of winner. (583.47)
3. replace3 score: 44.42% speed of winner. (555.92)
4. replace4 score: 20% speed of winner. (250.28)

Chrome on Galaxy Note 4

1. replace4 score: 100% *winner* (99.82)
2. replace1 score: 91.04% speed of winner. (90.88)
3. replace3 score: 70.27% speed of winner. (70.15)
4. replace2 score: 38.25% speed of winner. (38.18)

F
Frank W. Zammetti

I tried a number of these suggestions after realizing that an implementation I had written of this probably close to 10 years ago actually didn't work completely (nasty production bug in an long-forgotten system, isn't that always the way?!)... what I noticed is that the ones I tried (I didn't try them all) had the same problem as mine, that is, they wouldn't replace EVERY occurrence, only the first, at least for my test case of getting "test....txt" down to "test.txt" by replacing ".." with "."... maybe I missed so regex situation? But I digress...

So, I rewrote my implementation as follows. It's pretty darned simple, although I suspect not the fastest but I also don't think the difference will matter with modern JS engines, unless you're doing this inside a tight loop of course, but that's always the case for anything...

function replaceSubstring(inSource, inToReplace, inReplaceWith) {

  var outString = inSource;
  while (true) {
    var idx = outString.indexOf(inToReplace);
    if (idx == -1) {
      break;
    }
    outString = outString.substring(0, idx) + inReplaceWith +
      outString.substring(idx + inToReplace.length);
  }
  return outString;

}

Hope that helps someone!


Wont work if inToReplace is a substring of inReplaceWith. Infinite loop.
M
MadHatter
// Find, Replace, Case
// i.e "Test to see if this works? (Yes|No)".replaceAll('(Yes|No)', 'Yes!');
// i.e.2 "Test to see if this works? (Yes|No)".replaceAll('(yes|no)', 'Yes!', true);
String.prototype.replaceAll = function(_f, _r, _c){ 

  var o = this.toString();
  var r = '';
  var s = o;
  var b = 0;
  var e = -1;
  if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); }

  while((e=s.indexOf(_f)) > -1)
  {
    r += o.substring(b, b+e) + _r;
    s = s.substring(e+_f.length, s.length);
    b += e+_f.length;
  }

  // Add Leftover
  if(s.length>0){ r+=o.substring(o.length-s.length, o.length); }

  // Return New String
  return r;
};

This routine is the second fastest on Chrome but it takes 4.5 times the speed of fastest on Firefox, check my answer: stackoverflow.com/a/57697050/236062
j
jmealy

Use the replace() method of the String object.

As mentioned in the selected answer, the /g flag should be used in the regex, in order to replace all instances of the substring in the string.


This only replaces the first occurence!
@Dr. Hfuhruhurr - it can also replace all matches, if the /g option is used, as specified by the replace() method documentation (w3schools.com/jsref/jsref_obj_regexp.asp for example). Just because I did not explicitly mention the /g option does not make my answer any less valid.
S
Surya R Praveen

@Gumbo adding extra answer - user.email.replace(/foo/gi,"bar");

/foo/g - Refers to the all string to replace matching the case sensitive

/foo/gi - Refers to the without case sensitive and replace all For Eg: (Foo, foo, FoO, fOO)

DEMO