Weg, um String.Replace nur "ganze Wörter" zu haben

76

Ich brauche einen Weg, um dies zu haben:

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

gib dies zurück:

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

Grundsätzlich möchte ich ganze Wörter ersetzen, aber keine Teilübereinstimmungen.

HINWEIS: Ich muss dafür VB verwenden (SSRS 2008-Code), aber C # ist meine normale Sprache, daher sind die Antworten in beiden Sprachen in Ordnung.

Vaccano
quelle
Dies ist hier dupliziert, denke ich: stackoverflow.com/questions/1209049/regex-match-whole-words
James Michael Hare
Ich denke, der einfachste Weg (möglicherweise nicht der beste Weg) wäre, am Anfang und am Ende des Suchbegriffs ein Leerzeichen einzufügen, um beispielsweise ganze Wörter zu ersetzen, und nach "ertrinken" zu suchen, damit solche Dinge nicht ersetzt werden als "Ertrinken".
jay_t55

Antworten:

125

Ein Regex ist der einfachste Ansatz:

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

Der wichtige Teil des Musters ist das \bMetazeichen, das an Wortgrenzen übereinstimmt. Wenn Sie die Groß- und Kleinschreibung nicht berücksichtigen müssen RegexOptions.IgnoreCase:

Regex.Replace(input, pattern, replace, RegexOptions.IgnoreCase);
Ahmad Mageed
quelle
14
Wo \bist Regex für Wortgrenzen sprechen.
Oded
6
Ihre Lösung ist großartig! Wenn ich viele poste einen FN-Wrapper mit Regex entkommen:static string ReplaceFullWords( string input, string from, string to) { if (input == null) { return null; } return Regex.Replace(input, "\\b" + Regex.Escape(from) + "\\b", to); }
Stephanie
Die Linie sollte seinstring pattern = "\\btest\\b";
Valamas
23

Ich habe eine Funktion erstellt (siehe Blog-Beitrag hier ), die den von Ahmad Mageed vorgeschlagenen regulären Ausdruck umschließt

/// <summary>
/// Uses regex '\b' as suggested in /programming/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;
}
Michael Freidgeim
quelle
5
Denken Sie daran , zu verwenden , Regex.Escape()auf wordToFindso Sonderzeichen als normale Zeichen interpretiert werden.
CheeseSucker
@MichaelFreidgeim, Regex.Escape () macht einen großen Unterschied, wenn wordToFind mehr als alphanumerisch ist. Versuchen Sie beispielsweise, nach einem maskierten Schimpfwort "!% @ # \" Zu suchen. Es wird einfach nicht wie erwartet funktionieren.
Jroonk
@Jroonk, du kannst den Beitrag gerne bearbeiten, wenn er die Antwort verbessert
Michael Freidgeim
7

Wie von Sga kommentiert, ist die Regex-Lösung nicht perfekt. Und ich denke auch nicht leistungsfreundlich.

Hier ist mein Beitrag:

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

... mit Testfällen:

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" ) );
Alexis Pautrot
quelle
1
@Alexis, Sie sollten die Funktion in ReplaceWhitespaceSeparatedSubstrings umbenennen. Bitte geben Sie auch den Kommentar zur erwarteten Ausgabe für jeden Testfall an. Wenn Sie einen Leistungsvergleich mit dem Regex-Ansatz durchgeführt haben, teilen Sie diese bitte mit.
Michael Freidgeim
Führen Sie einfach die Testfälle aus, um die Ausgabeergebnisse anzuzeigen.
Alexis Pautrot
1
Dies ist kein "Leerzeichen getrennt", sondern ein "beliebiges Zeichen, kein Buchstabe oder keine Zahl". Nein, ich habe keine Perf-Vergleiche gemacht.
Alexis Pautrot
1
Ich habe damit gearbeitet und einen Fehler festgestellt: a = "4.99"; Console.WriteLine (a.ReplaceWholeWord ("9", "8.99")); ergibt 4.98.99. In diesem Zusammenhang sieht dies wie ein dummes Beispiel aus, aber es zeigt ein Problem, das ich bei einem realen Projekt habe.
Walter Williams
6

Ich möchte nur einen Hinweis zu diesem bestimmten Regex-Muster hinzufügen (wird sowohl in der akzeptierten Antwort als auch in der ReplaceWholeWord- Funktion verwendet). Es funktioniert nicht, wenn das, was Sie ersetzen möchten, kein Wort ist .

Hier ein Testfall:

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

(bereit, Code zu versuchen: http://ideone.com/2Nt0A )

Dies muss besonders berücksichtigt werden, wenn Sie Stapelübersetzungen durchführen (wie ich es für einige i18n-Arbeiten getan habe).

Sga
quelle
Es wird erwartet. "Doin '" ist kein "ganzes Wort". Sie versuchen, "durch Leerzeichen getrennte Teilzeichenfolgen" zu ersetzen
Michael Freidgeim
1

Wenn Sie definieren möchten, aus welchen Zeichen ein Wort besteht, z. B. "_" und "@"

Sie könnten meine (vb.net) Funktion verwenden:

 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

Prüfung

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

Ergebnis

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

Ich mag Regex nicht, weil es langsam ist. Meine Funktion ist schneller.

public static string ReplaceWholeWord(this string text, string word, string bywhat)
{
    static bool IsWordChar(char c) => char.IsLetterOrDigit(c) || c == '_';
    StringBuilder sb = null;
    int p = 0, j = 0;
    while (j < text.Length && (j = text.IndexOf(word, j, StringComparison.Ordinal)) >= 0)
        if ((j == 0 || !IsWordChar(text[j - 1])) &&
            (j + word.Length == text.Length || !IsWordChar(text[j + word.Length])))
        {
            sb ??= new StringBuilder();
            sb.Append(text, p, j - p);
            sb.Append(bywhat);
            j += word.Length;
            p = j;
        }
        else j++;
    if (sb == null) return text;
    sb.Append(text, p, text.Length - p);
    return sb.ToString();
}
Palota
quelle
-1

Sie könnten die Zeichenfolge.replace verwenden

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();
Alex
quelle
6
Ich bin kein Experte für C #, aber wie replacewird sich nicht ändern testing, textingwie in der Frage gestellt?
König Midas