2014-10-10 26 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

相關問題