web-dev-qa-db-fra.com

Manière d'avoir String.Replace seulement frappé "mots entiers"

J'ai besoin d'un moyen d'avoir ceci:

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

retournez ceci:

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

En gros, je veux remplacer les mots entiers, mais pas les correspondances partielles.

REMARQUE: je vais devoir utiliser VB pour cela (code SSRS 2008), mais C # est mon langage normal. Les réponses dans les deux cas sont correctes.

61
Vaccano

Une expression régulière est l'approche la plus simple:

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 partie importante du motif est le métacaractère \b, qui correspond aux limites de Word. Si vous en avez besoin, utilisez la méthode RegexOptions.IgnoreCase:

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

J'ai créé une fonction (voir billet de blog ici ) qui enveloppe l'expression regex, suggérée par Ahmad Mageed

/// <summary>
/// Uses regex '\b' as suggested in https://stackoverflow.com/questions/6143642/way-to-have-string-replace-only-hit-whole-words
/// </summary>
/// <param name="original"></param>
/// <param name="wordToFind"></param>
/// <param name="replacement"></param>
/// <param name="regexOptions"></param>
/// <returns></returns>
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;
}
16
Michael Freidgeim

Comme l'a commenté Sga, la solution regex n'est pas parfaite. Et je suppose que la performance n’est pas favorable non plus.

Voici ma contribution:

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

... Avec cas de test:

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

Je veux juste ajouter une note sur ce motif de regex particulier (utilisé à la fois dans la réponse acceptée et dans la fonction ReplaceWholeWord) . Cela ne fonctionne pas si ce que vous essayez de remplacer n'est pas un Word.

Voici un cas de test:

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

(prêt à essayer le code: http://ideone.com/2Nt0A )

Ceci doit être pris en compte spécialement si vous effectuez des traductions par lots (comme je l’ai fait pour certains travaux de i18n).

4
Sga

Si vous voulez définir quels caractères constituent un mot, c'est-à-dire "_" et "@"

vous pouvez utiliser ma fonction (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

Test

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

Résultat

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