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

时间:2014-10-10 11:59:15

标签: c# .net

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

我目前正在这样做:

string oldFormat = "01/01/2014";
string newFormat = oldFormat.Replace("/", "");

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


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

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

我已经使用StringBuilder来创建输出文件。我也知道discarded strings will be garbage collected(在时间结束之前的某个时刻)。我想知道是否有更好,更有效的方法来替换字符串中所有出现的特定字符/子字符串,这不会另外创建字符串。

4 个答案:

答案 0 :(得分:6)

  

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

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

<强>为什么吗

没有理由在每次替换时创建新的字符串 - 避免使用它非常简单,并且会带来巨大的性能提升。

如果您非常感兴趣referencesource.microsoft.comSSCLI2.0源代码将证明这一点(how-to-see-code-of-method-which-marked-as-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
    }
}

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

  

我想知道是否有更好,更有效的替换方式   字符串中出现的所有特定字符/子字符串   不另外创建一个字符串。

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

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

答案 1 :(得分:4)

嗯,我不是.NET开发团队成员(不幸的是),但我会尝试回答你的问题。

Microsoft有一个很棒的.NET参考源代码站点,而according to itString.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

现在,如果我们按照StringBuilder.Replace实施,我们就会看到它实际上在内部做了什么。

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

虽然String在.NET中是不可变的,但这不是某种限制,它是合同。 String实际上是一个引用类型,它包含的是实际字符串的长度+字符的缓冲区。你实际上可以获得一个指向这个缓冲区的不安全指针,并在运行中更改它#34;但是我不建议这样做。

现在,StringBuilder类还包含一个字符数组,当您将字符串传递给其构造函数时,它实际上将字符串的缓冲区复制到自己的缓冲区(请参阅参考源)。但它没有的是不可变性的契约,所以当你使用StringBuilder修改一个字符串时,你实际上正在使用char数组。请注意,当您在StringBuilder上调用ToString()时,它会创建一个新的&#34; immutable&#34;字符串任何复制他的缓冲区。

因此,如果您需要一种快速且内存有效的方法来对字符串进行更改,那么StringBuilder绝对是您的选择。特别是关于Microsoft明确地recommends使用StringBuilder,如果你&#34; 对字符串重复修改&#34;。

答案 2 :(得分:0)

我还没有找到任何消息来源,但我强烈怀疑实现会创建新的字符串。我也在内部使用StringBuilder实现它。如果你想更换一个巨大的字符串,那么String.Replace绝对没问题。但是如果你必须多次更换它,你应该考虑使用StringBuilder.Replace,因为Replace的每次调用都会创建一个新的字符串。

因此您可以使用StringBuilder.Replace,因为您已使用StringBuilder

答案 3 :(得分:0)

没有字符串方法。你是自己的。但你可以尝试这样的事情:

oldFormat="dd/mm/yyyy";

string[] dt = oldFormat.Split('/');
string newFormat = string.Format("{0}{1}/{2}", dt[0], dt[1], dt[2]);

StringBuilder sb = new StringBuilder(dt[0]);
sb.AppendFormat("{0}/{1}", dt[1], dt[2]);