【问题标题】:Edit characters in a String in C#在 C# 中编辑字符串中的字符
【发布时间】:2011-02-25 06:06:17
【问题描述】:

在 C# 中编辑字符串中的字符最简洁的方法是什么?

C++ 中的 C# 等价物是什么:

std::string myString = "boom";
myString[0] = "d";

【问题讨论】:

  • 鉴于不变性问题,我认为最佳答案取决于您对字符串的了解以及哪些字符可能会更改以及更改频率。
  • @Chris Farmer:实际上我怀疑它会大大改变答案。任何性能合理的实现都将使用 char 数组或 StringBuilder。哪个是“最好的”主要是口味问题。
  • @hemp:我不反对。在看到 StringBuilder 建议之前,我已经写了我的评论。我认为这可能是处理几乎所有这些情况的好方法。

标签: c# .net c++ string


【解决方案1】:

字符串在 C# 中是不可变的。使用 StringBuilder 类或字符数组。

StringBuilder sb = new StringBuilder("boom");
sb[0] = 'd';

IIRC,.NET 使用所谓的字符串池。每次创建新的字符串文字时,它都会作为字符串池的一部分存储在内存中。如果创建第二个字符串与字符串池中的字符串匹配,则两个变量将引用相同的内存。

当您尝试使用 .NET 中的字符串将“b”字符替换为“d”字符时,您的程序实际上是在字符串池中创建第二个字符串,其值为“doom” ,尽管从您的角度来看,这似乎根本没有发生。通过阅读代码,可以假设字符正在被替换。

我提出这个问题是因为我一直遇到这个问题,而且人们经常问为什么当字符串可以做同样的事情时他们应该使用 StringBuilder。从技术上讲它不能,但它的设计方式看起来好像可以。

【讨论】:

  • 感谢您提供的信息。 - 字符串池中是否创建了 StringBuilder 字符串?
【解决方案2】:

决定在两种最典型的方法中记录我的感受,再加上一种我认为没有代表的方法;这是我发现的(发布版本):

替换字符:86 毫秒
ReplaceAtSubstring:258 毫秒
ReplaceAtStringBuilder:161ms

显然,Char 数组方法在运行时得到了最好的优化。这实际上表明当前的领先答案(StringBuilder)可能不是最佳答案。

这是我使用的测试:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("ReplaceAtChars: " + new Stopwatch().Time(() => "test".ReplaceAtChars(1, 'E').ReplaceAtChars(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtSubstring: " + new Stopwatch().Time(() => "test".ReplaceAtSubstring(1, 'E').ReplaceAtSubstring(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtStringBuilder: " + new Stopwatch().Time(() => "test".ReplaceAtStringBuilder(1, 'E').ReplaceAtStringBuilder(3, 'T'), 1000000) + "ms");
    }
}

public static class ReplaceAtExtensions
{
    public static string ReplaceAtChars(this string source, int index, char replacement)
    {
        var temp = source.ToCharArray();
        temp[index] = replacement;
        return new String(temp);
    }

    public static string ReplaceAtStringBuilder(this string source, int index, char replacement)
    {
        var sb = new StringBuilder(source);
        sb[index] = replacement;
        return sb.ToString();
    }

    public static string ReplaceAtSubstring(this string source, int index, char replacement)
    {
        return source.Substring(0, index) + replacement + source.Substring(index + 1);
    }
}

public static class StopwatchExtensions
{
    public static long Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Reset();
        sw.Start();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.ElapsedMilliseconds;
    }
}

【讨论】:

    【解决方案3】:

    这是我整理的一个有趣的。现在,请记住这不是很有效,特别是对于简单的替换。但是,编写起来很有趣,并且适合于相当易读的使用模式。它还强调了一个鲜为人知的事实,即 String 实现了 IEnumerable。

    public static class LinqToStrings
    {
        public static IQueryable<char> LinqReplace(this string source, int startIndex, int length, string replacement)
        {
            var querySource = source.AsQueryable();
            return querySource.LinqReplace(startIndex, length, replacement);
        }
    
        public static IQueryable<char> LinqReplace(this IQueryable<char> source, int startIndex, int length, string replacement)
        {
            var querySource = source.AsQueryable();
            return querySource.Take(startIndex).Concat(replacement).Concat(querySource.Skip(startIndex + length));
        }
    
        public static string AsString(this IQueryable<char> source)
        {
            return new string(source.ToArray());
        }
    }
    

    下面是一些用法示例:

    public void test()
    {
        var test = "test";
        Console.WriteLine("Old: " + test);
        Console.WriteLine("New: " + test.LinqReplace(0, 4, "SOMEPIG")
                                        .LinqReplace(4, 0, "terrific")
                                        .AsString());
    }
    

    输出:

    旧:测试
    新:SOMEterrificPIG

    同样的方法的另一个版本,它不是那么慢,直接使用 Substring:

    public static string ReplaceAt(this string source, int startIndex, int length, string replacement)
    {
        return source.Substring(0, startIndex) + replacement + source.Substring(startIndex + length);
    }
    

    还有一个很好的例子来说明为什么你应该分析你的代码,以及为什么你可能应该在生产代码中使用我的 LinqToStrings 实现,下面是一个时序测试:

    Console.WriteLine("Using LinqToStrings: " + new Stopwatch().Time(() => "test".LinqReplace(0, 4, "SOMEPIG").LinqReplace(4, 0, "terrific").AsString(), 1000));
    Console.WriteLine("Using Substrings: " + new Stopwatch().Time(() => "test".ReplaceAt(0, 4, "SOMEPIG").ReplaceAt(4, 0, "terrific"), 1000));
    

    在 1,000 次迭代中测量计时器滴答声,产生以下输出:

    使用 LinqToStrings:3,818,953 使用子字符串:1,157

    【讨论】:

    • 这很酷......但它并没有真正解决问题。他要修改,而不是插入。不过,我喜欢这种方法,而且你说得对,模式很好,性能……不太好!
    • 其实它确实解决了这个问题。由于它需要一个起始索引和长度,它可以用于插入、删除或替换。我的示例实际上显示了替换和插入(“test”变为“SOMEPIG”,然后变为“SOMEterrificPIG”。)也就是说 - 这不是我推荐的方法,除非你感到无聊!
    【解决方案4】:

    你不能,因为字符串是不可变的。

    你总是可以滚动你自己的扩展方法来做类似的事情:

    public static string ReplaceAtIndex(this string original, 
        int index, char character)
    {
        char[] temp = original.ToCharArray();
        temp[index] = character;
        return new String(temp);
    }
    

    然后这样称呼它:

    string newString = myString.ReplaceAtIndex(0, 'd');
    

    【讨论】:

    • 对该函数的每次调用都会创建 3 个临时字符串。这并不可怕,但如果你需要多次这样做,它会变得相当昂贵。
    • @hemp - 绝对从来没有说过这是最好的实现。呵呵。我会更新一些更好的东西。
    • +1 不错的答案 - 您在 C# 中使用的类似重载的构造是什么?
    • @David Relihan - 这称为扩展方法。你可以在这里阅读更多关于它们的信息:msdn.microsoft.com/en-us/library/bb383977.aspx
    【解决方案5】:

    请改用StringBuilder

    string is immutable as described by MSDN:

    字符串是不可变的——其内容 无法更改字符串对象 在创建对象之后,虽然 语法使它看起来好像你 可以做到这一点。

    所以你想要这样的东西:

     StringBuilder sb = new StringBuilder("Bello World!");
     sb[0] = 'H';
     string str = sb.ToString();
    

    【讨论】:

    • 得到了我的支持,因为这是唯一的其他答案,它提供了一种灵活的方式来真正正确地做他想做的事情,而无需昂贵或过度精心设计的结构。
    • @tesserex - 我不否认这是灵活的,但我几乎不会认为我的价格昂贵或过于精细。事实上,它和我的一样,只是它使用了更精细的构造。
    • @glowcoder - 你的答案和我的一样,所以你可以免于我之前的评论。我指的是使用替换或子字符串。
    • @Tesserex:我发布了一个不使用 Replace、Substring 或 StringBuilder 的帖子。 :)
    • @hemp - 我认为“过度阐述的结构”充分描述了你的答案:)
    【解决方案6】:

    正如布赖恩所说,改用StringBuilder

    string myString = "boom";
    StringBuilder myStringBuilder = new StringBuilder(myString);
    myStringBuilder[0] = 'd';
    // Whatever else you're going to do to it
    

    当您再次需要该字符串时,请致电myStringBuilder.ToString()

    【讨论】:

    • 是的 - 可能会这样
    【解决方案7】:
    string myString = "boom";
    char[] myChars = myString.ToCharArray();
    myChars[0] = 'd';
    myString = new String(myChars);
    

    【讨论】:

    • 您不能只在字符数组上调用 ToString()。转换为字符串不会重载。它只是给你“System.Char[]”
    • 是的,你是对的,这让我每次都明白。我已经进行了适当的编辑。谢谢。
    【解决方案8】:
    string myString = "boom";
    char[] arr = myString.ToCharArray();
    arr[0] = 'd';
    myString = new String(arr);
    

    【讨论】:

    • 我的时序测试表明,这个算法实际上比 StringBuilder 执行得更好。
    【解决方案9】:

    字符串是不可变的,所以你不能随便改变它。

    您可以从字符串中获取一个 char[],进行更改,然后从更改后的数组中创建一个新字符串。

    您可以使用诸如替换之类的东西,尽管它们不像 C 那样让您控制每个字符的基础。他们还会在每次更改时创建一个新字符串,其中 char[] 样式允许您进行所有更改,然后从中创建一个新字符串。

    【讨论】:

      【解决方案10】:

      尝试使用 string.replace

      http://msdn.microsoft.com/en-us/library/fk49wtc1.aspx

      可能是您最干净、最简单的方法。

      【讨论】:

      • 正在查看,但我的情况是,如果用于编辑字符串中的特定位置
      【解决方案11】:

      .Net 中的字符串是不可变的。这意味着您无法编辑它们。你所能做的就是制作新的。你必须这样做:

      string myString = "boom";
      myString = "d" + myString.Substring(1);
      

      【讨论】:

      • 这可行,但如果他必须编辑较长字符串中的多个字符,很快就会变得乏味。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多