2013-02-05 64 views
5

爲什麼line2只替換交替出現的一半?String.Replace不會替換所有匹配項

Dim line1 As String = "AAA|BBB|CCC|CCC|CCC|CCC|EEE|FFF" 
    Dim line2 As String = "AAA|BBB|CCC|CCC|CCC|CCC|EEE|FFF" 
    Dim line3 As String = "AAA|BBB|CCC|CCC|CCC|CCC|EEE|FFF" 

    line1 = line1.Replace("CCC", "") 
    line2 = line2.Replace("|CCC|", "||") 
    line3 = line3.Replace("CCC|", "|") 

結果:

line1 = "AAA|BBB|||||EEE|FFF" -- OK, but fails when element is "..|ZZZCCCZZZ|.." 
line2 = "AAA|BBB||CCC||CCC|EEE|FFF" -- Not OK 
line3 = "AAA|BBB|||||EEE|FFF" -- OK, but fails similar to Line1 edge-case for "..|ZZZCCC|.." 

我一直在使用正則表達式的嘗試,但得到了類似的結果。

下面真的沒有比這更好的方法嗎?

Do While line1.Contains("|CCC|") 
    line1 = line1.Replace("|CCC|", "||") 
Loop 
+3

一旦它找到的第一個標記,它開始尋找_next_一個_after_該令牌。因此,它找到'| CCC |',將其替換,然後繼續,它看到的第一件事是'CCC |',它不匹配。 –

回答

8

一旦它找到的第一個標記,它開始尋找後下一個一個該令牌。所以它找到|CCC|,取代它,然後繼續,它看到的第一件事是CCC|哪個不匹配。它不會預先掃描要替換的令牌的字符串。

考慮這樣的:

鑑於AAA|BBB|CCC|CCC|CCC|CCC|EEE|FFF

它運行到AAA|BBB|CCC|HOLD IT|CCC|被發現了,讓我們開始建立我們的字符串:

AAA|BBB + ||(我們更換)

現在讓我們來看看mov對,我們現在有CCC|CCC|CCC|EEE|FFF留下來與工作。

它運行到CCC|CCC|HOLD IT|CCC|被發現,讓我們繼續加入到我們的字符串:

AAA|BBB||CCC + ||(我們更換)

現在,讓我們繼續前進,我們現在有CCC|CCC|EEE|FFF和如此等等。

編輯:考慮描述的返回值上MSDN條目:

一個字符串,它相當於除了屬性oldValue的所有 實例與NEWVALUE替換當前字符串。

一個可能閱讀爲你期望它預掃描串並發現所有匹配。我在MSDN文檔中沒有看到描述這個角落案例的任何內容。也許這應該添加到MSDN文檔中。

+0

我可以理解爲什麼這會讓人困惑,因爲它不會修改原始字符串。這幾乎看起來像是一個實現細節,但是非常合乎邏輯。恥辱這不是在這裏提到:http://msdn.microsoft.com/en-us/library/fk49wtc1.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2 –

+3

@JasonSperske它doesn看起來並不令人困惑......至少在使用這種方法一次或兩次後不會:D匹配*消耗匹配的所有內容*和*跳過任何替換的文本。如果不是這種情況,那麼可能有無限的替換遞歸!例如'line1.Replace(「X」,「X」)' – 2013-02-05 18:01:27

+0

@pst - 這看起來像是一個邏輯優化,儘管它也可以通過計算'while找到,替換'執行的次數基於原始字符串的長度,模式和替換。 – Leon

3

而不是使用正則表達式或string.Replace您可以解析值,篩選你不想要的值並將它們連接在一起。

line1 = string.Join("|", line1.Split("|").Select(s => s == "CCC" ? "" : s).ToArray()); 

對不起,我不知道VB當量。

+0

僅僅因爲我很好奇:-) 這不是一個耗費內存的操作嗎?我的意思是......字符串操作基本上是內存消耗。 –

+0

@MatsMagnem那麼它肯定會創建一個字符串數組,然後是新的連接字符串,但所有這些都將用於垃圾收集。但是,這可能會導致超大字符串的內存不足錯誤。在這種情況下,你會想要一個緩衝類型的算法,但我認爲在這種情況下,字符串不會那麼大。 – juharr

1

對於未來的人,我已經添加一個擴展方法來克服框架這一限制:

<System.Runtime.CompilerServices.Extension()> 
Public Function ReplaceAll(ByVal original As String, ByVal oldValue As String, ByVal newValue As String) As String 

    If newValue.Contains(oldValue) Then 
     Throw New ArgumentException("New value can't be a subset of OldValue as infinite replacements can occur.", newValue) 
    End If 

    Dim maxIterations As Integer = original.Length \ oldValue.Length 

    While maxIterations > 0 AndAlso original.Contains(oldValue) 
     original = original.Replace(oldValue, newValue) 
     maxIterations -= 1 
    End While 

    Return original 

End Function 
+0

我認爲這不是「限制」..但如果這是你所需要的..但​​是,*我懷疑這會在某些情況下失敗(或「意外行爲」)*。特別是,在執行所有原始替換之前,最大迭代可能會過早耗盡。 – 2013-02-05 19:48:31

+0

(這將導致正確的無限遞歸情況,我在前面的評論中談到如果匹配可以匹配替換) – 2013-02-05 19:58:09

+0

如果新值在其中包含舊值'.Replace(「X」,「XX」 )拋出異常。 – Leon

0

我可能會使用一個regular expression replacelook-around這種情況。

考慮這個例子:

Regex.Replace("FCCCF|CCC|CCC|", "((?<=[|])CCC(?=[|]))", "") 
// -> 
"FCCCF|||" 

這將始終一致的正確的次數,不容易出現任何無限遞歸的問題。它需要修改適當的正則表達式並更改替換數據。

然而,每克里斯的評論請注意:

Regex.Replace("FCCCF|CCC|CCC||CCC|", "((?<=[|])CCC(?=[|]))", "") 
// -> only 5 pipes: verify this is correct per the intended semantics 
"FCCCF|||||" 
+2

正如我在評論中提到的那樣,當我期望FCCCF ||||||(5對6管道)時,用那個正則表達式「FCCCF | CCC | CCC || CCC |'產生'FCCCF |||||' 。編輯:也許在這一點上,我們正在從「基礎類庫」的領域,並進入「業務規則」無論如何:) –

+0

@ChrisSinclair感謝您指出 - 這是一點點意想不到的細節,可以咬的很難。 – 2013-02-05 20:16:38

相關問題