2014-10-10 25 views
2

我们有一个要求,包含dd/mm/yyyy格式的日期的字符串转换为ddmmyyyy格式(如果你想知道为什么我存储日期字符串中,我的软件处理大宗交易的文件,这是一个基于行的文本文件格式由银行使用)。是否string.Replace(string,string)创建附加字符串?

目前我正在这样做:

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

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


这也是为什么我问这个的原因:我处理事务文件大小不等,从几KB到几十兆

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

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

+0

你应该尝试使用Regex.Replace,并比较性能。我曾经从一个大小为1MB的文件中删除了不必要的NewLine字符,并且正则表达式有很大的不同(以分钟为单位......)虽然我必须执行条件替换和其他一些文本操作,所以我建议在这个确切的案例 – Arie 2014-10-10 12:06:52

+2

我认为它只为一个整个Replace分配一个字符串。没有一个字符串用于每个事件的替换。 – 2014-10-10 12:08:09

+0

'字符串ReplaceInternal'是外部实现的方法。我不认为我们能够知道发生了什么。 – 2014-10-10 12:12:09

回答

4

嘛,我不是一个.NET开发团队成员(不幸的),但我会尽量回答你的问题。

微软拥有一个很棒的.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对象

虽然String是不可改变的。NET,这不是某种限制,它是合同。字符串实际上是一个引用类型,它包含的是实际字符串+字符缓冲区的长度。实际上,你可以得到一个不安全的指向这个缓冲区的指针并且“随时”改变它,但是我不会推荐这样做。

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

因此,如果您需要一种快速且高效的内存方式来对字符串进行更改,那么StringBuilder肯定是您的选择。特别是关于微软明确recommends使用StringBuilder,如果你“重复修改字符串”。

+0

'String.Replace'的合约并不要求实现避免创建不必要的中间String对象,但当它很容易被避免时不太可能使用这样的实现。 – 2014-10-10 12:08:46

+0

所以我有和你我一样的答案,你在我面前答案...你得到了一个投票,我得到了一个投票.....什么给了?? – kjbartel 2014-10-10 12:12:50

+0

@kjbartel:你以什么方式回答这个问题?你说它总是创建一个新的字符串。但OP询问它是否为应该替换的每个字符串创建一个新的字符串,而不是每个'Replace'调用一次。这试图找到一个记录如何实现'String.Replace'的源文件。评论表明只有一个字符串被创建。 – 2014-10-10 12:17:04

0

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

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

+0

谢谢,事实证明我的问题是[XY问题](http://meta.stackexchange.com/q/66377/262588),并且您已经给出了一个很好的提示来解决X(高效替换)。但我也想知道Y的答案(如果替换多次出现创建多个字符串)。 – Krumia 2014-10-10 12:08:38

+1

@Krumia:我还没有找到任何资源,但我强烈怀疑这个实现总是会创建新的字符串。我会在内部使用StringBuilder来实现它。那么'String.Replace'绝对没问题,如果你想替换一个巨大的字符串。但是如果你必须多次替换它,你应该考虑使用'StringBuilder.Replace',因为每次调用'Replace'都会创建一个新的字符串(我会将这个注释添加到我的答案中)。 – 2014-10-10 12:10:07

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]); 
6

果然,这个转换 “2014年1月1日” 到 “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个字符的容量。避免任何内存分配。这只有在更换长度相等的情况下才是正确的,并且如果您处于紧密的环路中,可以为您带来不错的性能增益。

在编写任何东西之前,先用大文件运行基准测试,然后看看性能是否足够。如果表现够了 - 不要做任何事情。

+0

哇,我还没有看到CLI2.0的源代码...谢谢! – Alovchin 2014-10-10 12:33:40

+1

@Alovchin,是的,几个小时前我自己发现了它。它只有2.0,但肯定会给你带来好的想法发生了什么:-) – 2014-10-10 12:34:28

+0

@ChrisEelmaa你是如何找到['String.ReplaceInternal'方法](http://referencesource.microsoft.com/mscorlib/R/35ab9efe11757286.html)在CLI 2.0上调用此代码? – 2014-10-10 12:45:43

相关问题