Maneira de ter String.Replace só bateu “palavras inteiras”

Eu preciso de uma maneira de ter isso:

"test, and test but not testing. But yes to test".Replace("test", "text") 

devolva isto:

 "text, and text but not testing. But yes to text" 

Basicamente eu quero replace palavras inteiras, mas não parciais.

NOTA: Eu vou ter que usar o VB para isso (código SSRS 2008), mas o C # é o meu idioma normal, então as respostas estão bem.

Um regex é a abordagem mais fácil:

 string input = "test, and test but not testing. But yes to test"; string pattern = @"\btest\b"; string replace = "text"; string result = Regex.Replace(input, pattern, replace); Console.WriteLine(result); 

A parte importante do padrão é o metacaractere \b , que corresponde aos limites das palavras. Se você precisar usar maiúsculas e minúsculas, use RegexOptions.IgnoreCase :

 Regex.Replace(input, pattern, replace, RegexOptions.IgnoreCase); 

Eu criei uma function (veja o post do blog aqui ) que envolve a expressão regex, sugerida por Ahmad Mageed

 ///  /// Uses regex '\b' as suggested in https://stackoverflow.com/questions/6143642/way-to-have-string-replace-only-hit-whole-words ///  ///  ///  ///  ///  ///  static public string ReplaceWholeWord(this string original, string wordToFind, string replacement, RegexOptions regexOptions = RegexOptions.None) { string pattern = String.Format(@"\b{0}\b", wordToFind); string ret=Regex.Replace(original, pattern, replacement, regexOptions); return ret; } 

Como comentado por Sga, a solução de regex não é perfeita. E eu acho que não é também amigável ao desempenho.

Aqui está minha contribuição:

 public static class StringExtendsionsMethods { public static String ReplaceWholeWord ( this String s, String word, String bywhat ) { char firstLetter = word[0]; StringBuilder sb = new StringBuilder(); bool previousWasLetterOrDigit = false; int i = 0; while ( i < s.Length - word.Length + 1 ) { bool wordFound = false; char c = s[i]; if ( c == firstLetter ) if ( ! previousWasLetterOrDigit ) if ( s.Substring ( i, word.Length ).Equals ( word ) ) { wordFound = true; bool wholeWordFound = true; if ( s.Length > i + word.Length ) { if ( Char.IsLetterOrDigit ( s[i+word.Length] ) ) wholeWordFound = false; } if ( wholeWordFound ) sb.Append ( bywhat ); else sb.Append ( word ); i += word.Length; } if ( ! wordFound ) { previousWasLetterOrDigit = Char.IsLetterOrDigit ( c ); sb.Append ( c ); i++; } } if ( s.Length - i > 0 ) sb.Append ( s.Substring ( i ) ); return sb.ToString (); } } 

… Com casos de teste:

 String a = "alpha is alpha"; Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "alphonse" ) ); Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "alf" ) ); a = "alphaisomega"; Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "xxx" ) ); a = "aalpha is alphaa"; Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "xxx" ) ); a = "alpha1/alpha2/alpha3"; Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "xxx" ) ); a = "alpha/alpha/alpha"; Console.WriteLine ( a.ReplaceWholeWord ( "alpha", "alphonse" ) ); 

Eu só quero adicionar uma nota sobre este padrão regular de regex (usado tanto na resposta aceita como na function ReplaceWholeWord ). Não funciona se o que você está tentando replace não é uma palavra .

Aqui um caso de teste:

 using System; using System.Text.RegularExpressions; public class Test { public static void Main() { string input = "doin' some replacement"; string pattern = @"\bdoin'\b"; string replace = "doing"; string result = Regex.Replace(input, pattern, replace); Console.WriteLine(result); } } 

(pronto para experimentar o código: http://ideone.com/2Nt0A )

Isso deve ser levado em consideração especialmente se você estiver fazendo traduções em lote (como eu fiz para algum trabalho do i18n).

Se você quiser definir quais caracteres compõem uma palavra, ou seja, “_” e “@”

você poderia usar minha function (vb.net):

  Function Replace_Whole_Word(Input As String, Find As String, Replace As String) Dim Word_Chars As String = "ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz0123456789_@" Dim Word_Index As Integer = 0 Do Until False Word_Index = Input.IndexOf(Find, Word_Index) If Word_Index < 0 Then Exit Do If Word_Index = 0 OrElse Word_Chars.Contains(Input(Word_Index - 1)) = False Then If Word_Index + Len(Find) = Input.Length OrElse Word_Chars.Contains(Input(Word_Index + Len(Find))) = False Then Input = Mid(Input, 1, Word_Index) & Replace & Mid(Input, Word_Index + Len(Find) + 1) End If End If Word_Index = Word_Index + 1 Loop Return Input End Function 

Teste

 Replace_Whole_Word("We need to replace words tonight. Not to_day and not too well to", "to", "xxx") 

Resultado

 "We need xxx replace words tonight. Not to_day and not too well xxx" 

Você poderia usar o string.replace

 string input = "test, and test but not testing. But yes to test"; string result2 = input.Replace("test", "text"); Console.WriteLine(input); Console.WriteLine(result2); Console.ReadLine();