Effective solution to String.Replace?

Say I have an array of strings declared as follows:

String[] strings = new String[ 1024 ]; 

And given the array of key value pairs declared as follows:

 KeyValuePair<String, String>[] pairs = new KeyValuePair<String, String>[ 50 ]; 

What are the possible workarounds for a solution that would do this:

 for ( int i = 0; i < 1024; ++i ) { foreach ( var kv in pairs ) { strings[ i ] = strings[ i ].Replace( kv.Key, kv.Value ); } } 

This code is arbitrary and just shows what the actual problem is. Given the many key values ​​that are known at compile time, how can I make an efficient String.Replace and possibly reduce the method calls and copy the string around (each call to String.Replace will create a new immutable string, and not very effective when there are many such substitute calls)?

+4
source share
5 answers

This does not help with the number of loops, but if you use StringBuilder as an intermediate, it has a .Replace call with the same set of parameter signatures.

Edit:

Not sure if this is faster, but you can use Regex.Replace with a delegate evaluator.

If you create a search regular expression using your keys: (Key1 | key2 | key3 | Key4 ...)

and then pass the delegate to .Replace, you can return the search based on the Match Match property.

  public string ReplaceData(Match m) { return pairs[m.Value]; } 

...

  pairs.Add("foo","bar"); pairs.Add("homer","simpson"); Regex r = new Regex("(?>foo|homer)"); MatchEvaluator myEval = new MatchEvaluator(class.ReplaceData); string sOutput = r.Replace(sInput, myEval); 
+2
source

I would say to list strings, and then with each of the string wizards, execute replacement calls. This will save on the creation of additional immutable string objects. Most likely, a little overhead, if you only have a small number of replacements, but since you stated that there will be many, this should help.

+2
source

You can use the obtained tips (for example, use StringBuilder) and with parallel extensions, use the number of cores that you have on your computer for parallel operation.

Take a look at this code:

 class Program { static void Main(String[] args) { // Filling the data List<KeyValuePair<String, String>> map = new List<KeyValuePair<String, String>>(); List<StringBuilder> strings = new List<StringBuilder>(); List<StringBuilder> strings2 = new List<StringBuilder>(); for (Int32 i = 0; i < 50; i++) { String key = String.Format("[KEY{0}]", i); String value = String.Format("Text of KEY{0}", i); KeyValuePair<String, String> keyValuePair = new KeyValuePair<String, String>(key, value); map.Add(keyValuePair); } for (Int32 i = 0; i < 1024; i++) { StringBuilder text = new StringBuilder(); foreach (KeyValuePair<String, String> keyValuePair in map) { text.AppendFormat("Some text before - {0} - Some text after.", keyValuePair.Key); text.AppendLine(); } strings.Add(text); strings2.Add(text); } // Measuring the normal loop Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); foreach (StringBuilder text in strings) { foreach (KeyValuePair<String, String> eachMap in map) { text.Replace(eachMap.Key, eachMap.Value); } } stopwatch.Stop(); Console.WriteLine("Time with normal loop: {0}", stopwatch.Elapsed); // Measuring the parallel loop stopwatch.Reset(); stopwatch.Start(); Parallel.ForEach(strings2, text => { foreach (KeyValuePair<String, String> eachMap in map) { text.Replace(eachMap.Key, eachMap.Value); } }); stopwatch.Stop(); Console.WriteLine("Time with parallel: {0}", stopwatch.Elapsed); Console.ReadLine(); } } 

And look at some of the measures that run in my nootebook (AMD Turion64 X2 - 2 cores):




Normal loop time: 00: 00: 03.5956428

Time parallel: 00: 00: 01.8707367



Time with normal loop: 00: 00: 02.1467821

Time parallel: 00: 00: 01.4627365



Time with normal loop: 00: 00: 03.4123084

Time with parallel: 00: 00: 01.6704408






Hope this helps.

Ricardo Lacerda Castelo Branco

+1
source

This should do less string manipulation by choosing values

  String[] strings = new String[1024]; KeyValuePair<String, String>[] pairs = new KeyValuePair<String, String>[ 50 ]; String[] replaced = strings.Select(x => pairs.Any( y => y.Key == x ) ? pairs.Where( z => z.Key == x).Select( val => val.Value).First() : x ) .ToArray(); 
0
source

I think the suggestions of Joe and Agent_9191 are essentially the same:

 const int NUM_STRINGS = 1024; string[] strings = new string[NUM_STRINGS]; StringBuilder[] stringBuilders = new StringBuilder[NUM_STRINGS]; // ensure that all StringBuilder objects are initialized for (int i = 0; i < NUM_STRINGS; i++) { stringBuilders[i] = new StringBuilder(strings[i]); } KeyValuePair<string, string>[] pairs = new KeyValuePair<string, string>[50]; for (int i = 0; i < NUM_STRINGS; i++) { foreach (var kv in pairs) { // StringBuilder is mutable; // this actually modifies the instance stringBuilders[i].Replace(kv.Key, kv.Value); } strings[i] = stringBuilders[i].ToString(); } 
0
source

All Articles