【问题标题】:Mutable String Editing in C# / .NETC# / .NET 中的可变字符串编辑
【发布时间】:2009-10-15 16:10:17
【问题描述】:

我想在 .NET 应用程序中就地获取和编辑字符串。我知道StringBuilder 允许我进行就地追加、插入和替换,但它不允许像这样简单的方法:

while (script.IndexOf("@Unique", StringComparison.InvariantCultureIgnoreCase) != -1)
{
   int Location = script.IndexOf("@Unique", StringComparison.InvariantCultureIgnoreCase);
   script = script.Remove(Location, 7);
   script = script.Insert(Location, Guid.NewGuid().ToString());
}

因为StringBuilder 中没有IndexOf。有没有人有一种有效的方法来对文本信息进行就地编辑?

编辑#1: 更改示例以更明显地表明每个“替换”都需要有不同的结果。

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    如果您的代码真的这么简单,那么为什么不使用内置的Replace 方法之一,在stringStringBuilderRegex 上?

    编辑以下评论...

    您可以使用one of the overloads of Regex.Replace that takes a MatchEvaluator 参数将每个匹配项替换为单独的值:

    string foo = "blah blah @Unique blah @Unique blah blah @Unique blah";
    
    // replace each occurrence of "@Unique" with a separate guid
    string bar = Regex.Replace(foo, "@Unique",
        new MatchEvaluator(m => Guid.NewGuid().ToString()),
        RegexOptions.IgnoreCase));
    

    【讨论】:

    • 我的问题是,在上面的示例中,@Unique 的每个实例都需要不同的值,所以我无法在字符串中进行全局查找和替换。
    • @JasonRShaver:这可以使用Regex.Replace 来完成。看看我的编辑。
    • @Luke:这个解决方案看起来不错,但它是否支持 StringBuilder,如要求。猜猜“JasonRShaver”能够像问题中那样使用字符串来做到这一点。
    • 这是一个非常有趣的解决方案。我很好奇正则表达式处理器通过这样做在内存方面的表现如何。 MatchEvaluator 对我来说也是新的,很高兴现在“知道”它。以后我会用这个方法做一个测试应用,看看能不能完全解决问题。
    • @JasonRShaver 这些方法在内部使用StringBuilder(并进行一些子字符串化),因此应该相当有效。我刚刚意识到,necro-comment :-D
    【解决方案2】:

    你会做多少次替换?

    如果不是四个数字,那么就接受新的字符串实例,你可能过早地优化了......

    另一种解决方案...拆分“@uniqueID”,然后使用 StringBuilder 重新加入,为每次迭代添加分隔符。

    【讨论】:

    • 系统将在我们预期的高峰期处理大约 300,000 件商品。
    • @Jason - 这种方法在峰值负载下的性能规格是什么?执行一个实现并在加载时对其进行基准测试。如果符合规格,请发货。如果没有,请对其进行分析。 +1。
    【解决方案3】:

    StringBuilderReplace”方法怎么样:

    StringBuilder script;
    script.Replace("@Unique", GetGuidString());
    

    【讨论】:

    • 如果字符串有两个@Unique,那么两个替换将具有相同的值而不是唯一值。这就是他不想使用 IndexOf 的原因,因此每次替换都会生成一个新的唯一值。
    • 我不认为 StringBuilder 可以在这种情况下使用。您必须仅将其作为字符串处理,可能您可以在将其添加到 StringBuilder 之前对其进行处理。
    【解决方案4】:

    StringBuilder 的制作是为了让您可以轻松地添加到它,但代价是很难在其中搜索 - 尤其是索引它更困难(即更慢)。 如果您需要“就地”修改某些字符,最好在生成的字符串上进行。

    但是很难从您的问题中知道什么是适合您的正确答案,我的感觉是您不应该需要在 StringBuilder 中进行就地替换,而问题出在其他地方/您做错了其他事情。

    【讨论】:

      【解决方案5】:

      用户 Dennis 提供了一个IndexOf extension method for StringBuilder。有了这个,你应该可以用这种方式使用StringBuilder了。

      【讨论】:

        【解决方案6】:

        您可以使用字符串拆分来有效地执行此操作吗?

        类似:

        var sections = "a-@Unique-b-@Unique-c".Split(new string[] { "@Unique" }, StringSplitOptions.None);
        int i;
        StringBuilder builder = new StringBuilder();
        for(i = 0; i < sections.Length - 1; i++)
        {
            builder.Append(sections[i]);
            builder.Append(Guid.NewGuid().ToString());
        }
        builder.Append(sections[i]);
        
        Console.WriteLine(builder.ToString());
        Console.ReadKey(true);
        

        【讨论】:

          【解决方案7】:

          复杂但应该是高效的解决方案

              public StringBuilder Replace(this StringBuilder sb, string toReplace, Func<string> getReplacement)
              {
                  for (int i = 0; i < sb.Length; i++)
                  {
                      bool replacementFound = true;
                      for (int toReplaceIndex = 0; toReplaceIndex < toReplace.Length; toReplaceIndex++)
                      {
                          int sbIndex = toReplaceIndex + i;
                          if (sbIndex < sb.Length)
                          {
                              return sb;
                          }
                          if (sb[sbIndex] != toReplace[toReplaceIndex])
                          {
                              replacementFound = false;
                              break;
                          }
                      }
                      if (replacementFound)
                      {
                          string replacement = getReplacement();
                          // reuse the space of the toReplace string
                          for (int replacementIndex = 0; replacementIndex < toReplace.Length && replacementIndex < replacement.Length; replacementIndex++)
                          {
                              int sbIndex = replacementIndex + i;
                              sb[sbIndex] = replacement[i];
                          }
                          // remove toReplace string remainders
                          if (replacement.Length < toReplace.Length)
                          {
                              sb.Remove(i + replacement.Length, replacement.Length - toReplace.Length)
                          }
                          // insert chars not yet inserted
                          if (replacement.Length > toReplace.Length)
                          {
                              sb.Insert(i + toReplace.Length, replacement.ToCharArray(toReplace.Length, toReplace.Length - replacement.Length));
                          }
                      }
                  }
                  return sb;
              }
          

          用例

          var sb = new StringBuilder(script);
          
          script = sb.Replace("@Unique", () => Guid.NewGuid().ToString()).ToString();
          

          【讨论】:

            【解决方案8】:

            您将需要使用非托管代码块 就像声明一个指向你的字符串的指针并在内存中操作它一样简单。

            例子

            unsafe
            {
              char* ip;
              ip = &to_your_string;
            }
            

            【讨论】:

            • 看起来 unsafe 不允许您声明指向字符串的指针。对不起:(
            • -1。这是我几个月来见过的最可怕的 C# 代码。我什至不会考虑做这样的事情。我很高兴 .NET 设计者保证它不会工作。
            • @TrueWill:不幸的是,改变现有字符串非常容易。 string 类具有可以使用反射调用的私有 AppendInPlaceInsertInPlaceRemoveInPlace 方法。但我同意你的观点,甚至考虑在任何实际代码中使用这些方法都是疯狂的。
            • 抱歉,我不是故意让你哭的……但不安全是 .net 框架的一部分,而 .NET 设计者是设计它的人。它可能不适用于不同的字符串。
            猜你喜欢
            • 1970-01-01
            • 2011-02-25
            • 1970-01-01
            • 1970-01-01
            • 2015-04-15
            • 1970-01-01
            • 1970-01-01
            • 2020-08-18
            • 1970-01-01
            相关资源
            最近更新 更多