我们要求将包含dd/mm/yyyy
格式的日期的字符串转换为ddmmyyyy
格式(如果您想知道我为什么要在字符串中存储日期,我的软件会处理批量处理文件,这是银行使用的基于行的文本文件格式。
我目前正在这样做:
string oldFormat = "01/01/2014";
string newFormat = oldFormat.Replace("/", "");
果然,这会将"01/01/2014"
转换为"01012014"
。但我的问题是,替换是在一步中发生的,还是创建了一个中间字符串(例如:"0101/2014"
或"01/012014"
)?
这就是我问这个问题的原因:
我正在处理大小从几千字节到几百兆字节的事务文件。到目前为止,我还没有出现性能/内存问题,因为我仍在使用非常小的文件进行测试。但是当谈到兆字节时,我不确定我是否会遇到这些额外字符串的问题。我怀疑是因为string
s are immutable。有了数百万条记录,这些额外的内存消耗将大大增加。
我已经使用StringBuilder
来创建输出文件。我也知道discarded strings will be garbage collected(在时间结束之前的某个时刻)。我想知道是否有更好,更有效的方法来替换字符串中所有出现的特定字符/子字符串,这不会另外创建字符串。
答案 0 :(得分:6)
果然,这将“01/01/2014”转换为“01012014”。但我的问题 是,替换是一步完成,还是创建一个 中间字符串(例如:“0101/2014”或“01/012014”)?
否,它不会为每次替换创建中间字符串。但它确实创建了新的字符串,因为正如您所知,字符串是不可变的。
<强>为什么吗
没有理由在每次替换时创建新的字符串 - 避免使用它非常简单,并且会带来巨大的性能提升。
如果您非常感兴趣,referencesource.microsoft.com和SSCLI2.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 it,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
现在,如果我们按照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]);