2012-02-13 15 views
8

Mi sono chiesto quale sia il modo più semplice per sostituire una stringa di caratteri che deve essere sostituita successivamente.Sostituzione di caratteri errati di una stringa con caratteri non validi

Ad esempio:

var str = "[Hello World]"; 
//enclose all occurences of [ and ] with brackets[] 
str = str.Replace("[","[[]").Replace("]","[]]"); 
  • Il risultato desiderato: [[]Hello World[]]
  • Il risultato effettivo: [[[]]Hello World[]]

La ragione è ovviamente la seconda sostituire sulla corda già modificato.

Quindi, come sostituire tutte le occorrenze di caratteri "cattivi" con caratteri che contengono caratteri "cattivi"?


Una misura rapida di tutti gli approcci ha rivelato che il StringBuilder è il modo più efficiente.

file di 190KB (tutti in millisecondi)

regexTime   40.5065 
    replaceTime   20.8891 
    stringBuilderTime 6.9776 

file di 7MB

regexTime   1209.3529   
    replaceTime   403.3985 
    stringBuilderTime 175.2583 

Tra l'altro, la diretta StringBuilder approccio da John è stato due volte più veloce come loApproccioda Sehe.

Ho fatto un'estensione fuori di esso:

public static String EncloseChars(this string input, char[] charsToEnclose, String leftSide, String rightSide) { 
    if (charsToEnclose == null || leftSide == null || rightSide == null) 
     throw new ArgumentException("Invalid arguments for EncloseChars", charsToEnclose == null ? "charsToEnclose" : leftSide == null ? "leftSide" : "rightSide"); 
    Array.Sort(charsToEnclose); 
    StringBuilder sb = new StringBuilder(); 
    foreach (char c in input) { 
     if (Array.BinarySearch(charsToEnclose, c) > -1) 
      sb.Append(leftSide).Append(c).Append(rightSide); 
     else 
      sb.Append(c); 
    } 
    return sb.ToString(); 
} 

"[Hello World]".EncloseChars(new char[]{'[', ']'},"[","]"); 
+3

+1 Ben misurato, Tim. –

risposta

5

Ecco un modo molto uncool per farlo. Ma ha il vantaggio di essere abbastanza vicino a infallibile, credo, e di non usare regex (nel caso in cui preferiresti non usare regex).

StringBuilder sb = new StringBuilder(); 
foreach (char c in str.ToCharArray()) { 
    if (c == '[' || c == ']') { 
     sb.Append('[' + c + ']'); 
    } 
    else { 
     sb.Append(c); 
    } 
} 
string result = sb.ToString(); 
+0

Grazie. Spesso il modo uncool è il migliore/il più veloce. Nessun problema se nascosto in un metodo di estensione;) –

4

Che dire:

str = str.Replace("[", "$1[$2") 
     .Replace("]", "$1]$2") 
     .Replace("$1", "[") 
     .Replace("$2", "]"); 
+2

Bella idea, utilizzando i valori del monitor come intermediari. – Oded

+4

Ma dovresti controllare se la stringa non contiene già questi valori di monitoraggio, o lo stesso accade di nuovo. Esempio: 'Hello [$ 1]' – Oliver

+0

La modifica per sostituire i valori del monitor + i caratteri effettivi prima di sostituire i valori effettivi dei caratteri può risolvere il problema di Oliver (anche se in un modo muri-e-ladder), ma a scapito della leggibilità , Credo. –

1

ne dite:

char[] replacedChars = str.SelectMany(ch => 
            (ch == '[' ? new char[] {'[', '[', ']'} : 
            (ch == ']' ? new char[] {'[', ']', ']'} : 
            new char[] {ch}))).ToArray(); 
string replaced = new string(replacedChars); 

Si noti che questo evita il problema cicli multipli, ma crea almeno tanti array quanti sono i caratteri nella stringa di input, quindi potrebbe non essere ottimale in termini di prestazioni.

+0

+1 Crea l'uso di LINQ. Non molto performante ... :) – sehe

3

Che dire di questo elegante normale approccio espressione: prova

Regex.Replace("[Hello World]", @"[\[\]]", "[$0]"); 

Unità esso?

[TestMethod] 
public void UnitTestThat() 
{ 
    Assert.AreEqual(@"[[]Hello World[]]", Regex.Replace("[Hello World]", @"[\[\]]", "[$0]")); 
} 

Prova superata


Modifica @JohnMcGrant

Ecco una versione leggermente meno inefficiente del codice, che ha, tra l'altro, esattamente lo stesso comportamento come espressione regolare di cui sopra:

string result = input.Aggregate(new StringBuilder(), (a, c) => 
    -1 != "[]".IndexOf(c) ? a.AppendFormat("[{0}]", c) : a.Append(c)).ToString(); 
+0

Probabilmente si può presumere che ciò non succederà, ma cosa succederebbe se il testo tra parentesi potesse legittimamente contenere parentesi, magari sfuggire in qualche modo. Non spezzerebbe la regex? –

+0

@JohnMGant: No, perché ciò significa che i requisiti sono cambiati. E se la domanda fosse diversa? Questo avrebbe rotto la risposta? No. – sehe

+0

Aggiunto approccio alternativo basato sulla versione di @ JohnMGant. Penso che la regex sia ancora l'approccio migliore :) – sehe

1
StringBuilder result = new StringBuilder(); 

    foreach (Char singleCharacter in str) 
    { 
     result.Append(singleCharacter.Equals('[') ? "[[]" : singleCharacter.Equals(']') ? "[]]" : singleCharacter.ToString()); 
    } 

    str = result.ToString(); 
0

Ho avuto esattamente lo stesso problema, quindi ho fatto una funzione di supporto per fare proprio questo

protected string ReplaceUsingDictionary(string subject, Dictionary<string,string> pairs) 
    { 
     StringBuilder sb = new StringBuilder(subject); 

     sb.Replace("{", "{{").Replace("}", "}}"); 

     int i=0; 
     foreach (string key in pairs.Keys.ToArray()) 
     { 
      sb.Replace(
       key.Replace("{", "{{").Replace("}", "}}"), 
       "{" + i + "}" 
      ); 

      i++; 
     } 

     return string.Format(sb.ToString(), pairs.Values.ToArray()); 
    } 

// usage 
Dictionary<string, string> replacements = new Dictionary<string, string>(); 
replacements["["] = "[[]"; 
replacements["]"] = "[]]"; 

string mystr = ReplaceWithDictionary("[HelloWorld]", replacements); // returns [[]HelloWorld[]] 
Problemi correlati