string.Replace(string,string)是否会创建其他字符串?

sam*_*ris 2 .net c#

我们需要将包含dd/mm/yyyy格式日期的字符串转换为ddmmyyyy格式(如果您想知道我为什么要在字符串中存储日期,我的软件会处理批量事务文件,这是一种基于行的文本文件格式,由银行使用).

我目前正在这样做:

string oldFormat = "01/01/2014";
string newFormat = oldFormat.Replace("/", "");
Run Code Online (Sandbox Code Playgroud)

果然,这转化"01/01/2014""01012014".但我的问题是,替换是在一步中发生的,还是它创建了一个中间字符串(例如:"0101/2014""01/012014")?


这就是我问这个问题的原因:

我正在处理大小从几千字节到几百兆字节的事务文件.到目前为止,我还没有出现性能/内存问题,因为我仍在使用非常小的文件进行测试.但是当谈到兆字节时,我不确定我是否会遇到这些额外字符串的问题.我怀疑情况会是这样,因为strings是不可变的.有了数百万条记录,这种额外的内存消耗将大大增加.

我已经在使用StringBuilders来创建输出文件.而且我也知道丢弃的字符串将被垃圾收集(在时间结束之前的某个时刻).我想知道是否有更好,更有效的方法来替换字符串中所有出现的特定字符/子字符串,这不会另外创建字符串.

Ert*_*maa 6

果然,这将"01/01/2014"转换为"01012014".但我的问题是,替换是一步完成的,还是创建一个中间字符串(例如:"0101/2014"或"01/012014")?

,它不会为每个替换创建中间字符串.但它确实创建了新的字符串,因为正如您所知,字符串是不可变的.

为什么?

没有理由在每次替换时创建新的字符串 - 避免它非常简单,并且它将提供巨大的性能提升.

如果您对此非常感兴趣,那么referencesource.microsoft.comSSCLI2.0源代码将证明这一点(如何查看代码的方法 - 标记为methodimploptions-internalcall):

FCIMPL3(Object*, COMString::ReplaceString, StringObject* thisRefUNSAFE, 
          StringObject* oldValueUNSAFE, StringObject* newValueUNSAFE)
{

   // unnecessary code ommited
      while (((index=COMStringBuffer::LocalIndexOfString(thisBuffer,oldBuffer,
             thisLength,oldLength,index))>-1) && (index<=endIndex-oldLength))
    {
        replaceIndex[replaceCount++] = index;
        index+=oldLength;
    }

    if (replaceCount != 0)
    {
        //Calculate the new length of the string and ensure that we have 
        // sufficent room.
        INT64 retValBuffLength = thisLength - 
            ((oldLength - newLength) * (INT64)replaceCount);

        gc.retValString = COMString::NewString((INT32)retValBuffLength);
     // unnecessary code ommited
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,retValBuffLength是计算出来的,它知道了数量replaceCount.对于.NET 4.0 ,实际的实现可能有点不同(SSCLI 4.0没有发布),但我向你保证它没有做任何愚蠢的事情:-).

我想知道是否有更好,更有效的方法来替换字符串中所有出现的特定字符/子字符串,这不会另外创建字符串.

是.可重用StringBuilder,容量约为2000个字符.避免任何内存分配.只有当替换长度相等时才会出现这种情况,并且如果您处于紧密循环中,可以获得良好的性能提升.

在编写任何内容之前,请使用大文件运行基准测试,并查看性能是否足够.如果表现足够 - 不要做任何事情.


Alo*_*hin 5

好吧,不幸的是,我不是.NET开发团队的成员,但是我将尽力回答您的问题。

微软.NET的参考源代码一个伟大的网站,并根据它String.Replace调用,没有工作的外部方法。我不会争论它是如何实现的,但是对此方法有一点评论可以回答您的问题:

// This method contains the same functionality as StringBuilder Replace. The only difference is that
// a new String has to be allocated since Strings are immutable
Run Code Online (Sandbox Code Playgroud)

现在,如果我们继续StringBuilder.Replace实施,我们将看到其实际作用。

关于字符串对象的更多信息

尽管String在.NET中是不变的,但这不是某种限制,它是一个合同。字符串实际上是一种引用类型,它包括的是实际字符串的长度+字符缓冲区。实际上,您可以获取一个不安全的指向此缓冲区的指针并“即时”更改它,但是我不建议您这样做。

现在,StringBuilder该类还包含一个字符数组,当您将字符串传递给其构造函数时,它实际上会将字符串的缓冲区复制到自己的缓冲区中(请参阅参考资料)。但是,它没有不变性,因此,当您使用StringBuilder修改字符串时,实际上是在使用char数组。请注意,当您在StringBuilder上调用ToString()时,它将在其缓冲区中的任何副本上创建一个新的“不可变”字符串。

因此,如果您需要一种快速且内存高效的方式来更改字符串,那么StringBuilder绝对是您的选择。特别是考虑到Microsoft “如果对字符串执行重复的修改 ” ,则明确建议使用StringBuilder 。