【问题标题】:An efficient solution to a String.Replace problem?String.Replace 问题的有效解决方案?
【发布时间】:2023-03-31 02:27:01
【问题描述】:

假设我有一个这样声明的字符串数组:

String[] strings = new String[ 1024 ];

给定一个键值对数组,声明如下:

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

解决方案有哪些可能的解决方法:

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

此代码只是任意的,仅用于显示实际问题。给定很多在编译时就知道的键值,我怎样才能做一个高效的 String.Replace,并可能减少方法调用和复制 String(每次调用 String.Replace 都会产生一个新的不可变字符串,而不是有很多这样的替换调用时效率很高)?

【问题讨论】:

  • 您想针对什么优化它:速度、内存或代码可读性?
  • 密钥对是否相同?
  • @zvolkov:我想优化速度,在这种特殊情况下,我认为速度和内存是并行的。可读性可以忽略,有利于其他 2。我现在可以阅读,但根本没有效率。

标签: c# .net string


【解决方案1】:

它对循环的数量没有帮助,但如果你使用 StringBuilder 作为中间体,它会有一个带有相同参数签名集的 .Replace 调用。

编辑:

不确定它是否更快,但您可以将 Regex.Replace 与评估者委托一起使用。

如果您使用密钥构建搜索正则表达式: (key1|key2|key3|key4...)

然后将delegate传递给.Replace,就可以根据Match的Value属性返回一个lookup。

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

【讨论】:

  • 您可以通过使用原子组来进一步加快速度:(?&gt;foo|homer),然后确保键按字母顺序排列。
  • 我会测试这个和 StringBuilder 的建议,看看哪个表现更好。明天我会告诉你的。
【解决方案2】:

我会说将字符串转储到一个列表中,然后使用每个字符串构建器执行替换调用。这将节省额外不可变字符串对象的创建。如果您只需要少量替代品,很可能会产生一点开销,但既然您已经说过会有很多替代品,那么这应该会有所帮助。

【讨论】:

  • StringBuilder 是个好建议。我会将它与正则表达式进行基准测试,看看哪个更好。明天我会告诉你的。
【解决方案3】:

您可以使用收到的建议(例如使用 StringBuilder)并使用 Parallel 扩展,使用您机器中的内核数量来并行完成工作。

看看这段代码:

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

看看我的笔记本(AMD Turion64 X2 - 2 cores)上运行的一些措施:



正常循环时间:00:00:03.5956428
并行时间:00:00:01.8707367

正常循环时间:00:00:02.1467821
并行时间:00:00:01.4627365

正常循环时间:00:00:03.4123084
并行时间:00:00:01.6704408



希望这会有所帮助。

里卡多·拉塞尔达·布兰科堡

【讨论】:

    【解决方案4】:

    这应该通过选择值来减少字符串操作

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

    【讨论】:

      【解决方案5】:

      我认为 Joe 和 Agent_9191 的建议基本相同:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-06
        • 2020-09-04
        • 2015-09-12
        • 1970-01-01
        • 1970-01-01
        • 2020-05-22
        • 1970-01-01
        相关资源
        最近更新 更多