2013-02-05 15 views
5

Neden line2 yalnızca gerçekleşen olayların yarısını değiştiriyor?String.Replace tüm eşleşmelerin yerini almıyor

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|", "|") 

Sonuç:

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|.." 

Ben RegEx kullanarak çalıştı, ancak benzer sonuçlar elde var.

Bundan daha iyi bir yolu yok mu, aşağıda? ilk belirteci bulduğunda

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

ilk belirteci bulduğunda o simge _after_ _next_ bir aramaya başlar. Böylece, | CCC | 'yi bulur, yerini alır, sonra devam eder ve gördüğü ilk şey, 'CCC |' ile eşleşmez. –

cevap

8

, bu belirteç sonra sonraki bir aramaya başlar. Yani |CCC|'u bulur, yerini alır, sonra devam eder ve gördüğü ilk şey, eşleşmeyen CCC|'dur. Değiştirmek için tokenleri arayan dizgiyi önceden taramaz.

şöyle düşünün:

O çalışır AAA|BBB|CCC|CCC|CCC|CCC|EEE|FFF

Verilen AAA|BBB|CCC|TUT BT|CCC| bulunmuştur, en bizim dize bina başlayalım:

AAA|BBB + || (bizim yedek)

Şimdi hadi mov edelim e üzerinde, şimdi çalışmak için CCC|CCC|CCC|EEE|FFF var.

O çalışır CCC|CCC|TUT BT|CCC| bulunmuştur, en bizim dizeye eklemeye devam edelim:

AAA|BBB||CCC + || (bizim yedek)

Şimdi

devam edelim, şimdi var CCC|CCC|EEE|FFF ve böylece vb.

DÜZENLEME: dönüş değeri tarif MSDN ilgili giriş göz önüne alındığında:

OldValue her örneklerini newValue ile ikame edilmesi haricinde, mevcut dizesine eşdeğer olan bir dize. could

Bir Eğer o beklediğiniz olarak dize önceden tarar ve tüm eşleşmeleri bulur okudum. MSDN belgesinde bu köşe durumunu açıklayan hiçbir şey göremiyorum. Belki de bu MSDN belgesine eklenecek bir şey.

+0

Bunun neden karmaşık olduğunu anlayabiliyorum, çünkü orijinal dizeyi değiştirmiyor. Bu neredeyse bir uygulama detayı gibi görünüyor, ancak oldukça mantıklı. Utanmadan buraya yazınız: http://msdn.microsoft.com/en-us/library/fk49wtc1.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2 –

+3

@JasonSperske Yapmıyor Hiç kafa karıştırıcı değil .. En azından bir kez veya iki kez böyle bir yöntem kullandıktan sonra: D Eşleşme * eşleştiği herşeyi tüketir * ve * değiştirilen herhangi bir metin üzerinde * atlar. Bu durumda olmasaydı, o zaman sonsuz değiştirme özyinesi olabilirdi! Örneğin. line1.Replace ("X", "X") ' –

+0

@pst - mantıksal bir optimizasyon gibi gözükse de," bulundu, değiştir "işlevinin bir kaçış sınırıyla yürütüldüğü sayının sayılmasıyla da yapılabilir. Orijinal dizgenin uzunluğuna, desenine ve değiştirilmesine göre. – Leon

3

Normal ifadeler veya string.Replace kullanmak yerine, değerleri ayrıştırıp, istemediğinizi filtreleyebilir ve birleştirebilirsiniz.

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

Maalesef VB eşdeğerini bilmiyorum.

+0

Soru sadece merak ettiğim için merak ediyorum :-) Bu bellek tüketen bir işlem olmayacak mı? Demek istediğim ... string işlemleri temelde bellek tüketiyor. –

+0

@MatsMagnem Peki, kesinlikle bir dizi dizge ve ardından yeni eklenen dizgi oluşturur, ancak bunların hepsi çöp toplama için hazır olur. Ama evet, bu son derece geniş bir dizi için bellek yetersizliğinden kaynaklanabilir. Bu durumda bir arabellek türü algoritma istersiniz, ancak bu durumda dizelerin o kadar büyük olmayacağını düşündüm. – juharr

1

gelecekte herkes için, ben çerçevesinde bu sınırlamayı aşmak için bir uzantısı yöntemi ekledikten:

<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

Bunun "sınırlama" olmadığını iddia ediyorum .. ama ihtiyacınız olan şey buysa .. Ancak, * bunun bazı durumlarda başarısız olacağını (veya "beklenmedik şekilde hareket etmesini") şüpheleniyorum. Özellikle, tüm orijinal değiştirmelerin gerçekleştirilmesinden önce, maksimum iterasyonların çok erken bittiği olasıdır. –

+0

(Ve bu, eğer önceki eşleşmelerde eşleşmenin eşleşmesiyle eşleşebiliyorsa, önceki yorumlarda bahsettiğim sonsuz yineleme durumuna yol açacaktır.) –

+0

Yeni değer, içinde eski bir değer içeriyorsa. .Replace ("X", "XX")) 'istisna atıldı. – Leon

0

bu durum için bir look-around ile regular expression replace kullanabilir.

bu örneği düşünün:

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

Bu her zaman kere doğru sayıda maç ve herhangi sonsuz özyinelemeler sorunları eğilimli değildir edecektir. Uygun bir düzenli ifadede değişiklik yapılmasını ve değiştirme verilerinin değiştirilmesini gerektirir.

Ancak Chris'in yorumun başına not:

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

Benim yorumumda da belirtildiği gibi, bu regex 'FCCCF | CCC | CCC || CCC |' ile, '' FCCCF ||||| 'beklenirken '' FCCCF ||||||' (5 vs 6 boru) beklenir . DÜZENLEME: Bu noktada belki de "temel sınıf kütüphanesi" alanından ve "iş kurallarına" doğru hareket ediyoruz :) –

+0

@ChrisSinclair Bunu işaret ettiğin için teşekkürler - bu gerçekten zor ısırılabilecek küçük beklenmedik detaylar. –

İlgili konular