Forma de tener String.Replace only hit “whole words”

Necesito una forma de tener esto:

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

devuelve esto:

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

Básicamente quiero reemplazar palabras completas, pero no coincidencias parciales.

NOTA: Voy a tener que usar VB para esto (código de SSRS 2008), pero C # es mi lenguaje normal, por lo que las respuestas en cualquiera de ellas están bien.

Una expresión regular es el enfoque más 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); 

La parte importante del patrón es el metacarácter \b , que coincide con los límites de las palabras. Si necesita que sea insensible a las mayúsculas y minúsculas, use RegexOptions.IgnoreCase :

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

Creé una función (ver la publicación del blog aquí ) que envuelve la expresión de expresiones regulares, 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 comentó Sga, la solución de expresiones regulares no es perfecta. Y supongo que no es muy amigable para el rendimiento.

Aquí está mi contribución:

 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 (); } } 

… Con casos de prueba:

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

Solo quiero agregar una nota sobre este patrón de expresiones regulares particular (utilizado tanto en la respuesta aceptada como en la función ReplaceWholeWord ). No funciona si lo que intentas reemplazar no es una palabra .

Aquí un caso de prueba:

 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); } } 

(código listo para probar: http://ideone.com/2Nt0A )

Esto debe tenerse en cuenta, especialmente si está realizando traducciones por lotes (como hice para algunos trabajos de i18n).

Si quieres definir qué personajes componen una palabra, es decir “_” y “@”

podrías usar mi función (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 

Prueba

 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" 

Puede usar la cadena.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();