Cách để có Chuỗi. Thay thế chỉ nhấn "toàn bộ từ"


76

Tôi cần một cách để có điều này:

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

trả lại cái này:

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

Về cơ bản, tôi muốn thay thế toàn bộ từ, nhưng không thay thế từng phần.

LƯU Ý: Tôi sẽ phải sử dụng VB cho việc này (mã SSRS 2008), nhưng C # là ngôn ngữ bình thường của tôi, vì vậy các câu trả lời bằng một trong hai đều ổn.


Điều này bị trùng lặp ở đây tôi nghĩ: stackoverflow.com/questions/1209049/regex-match-whole-words
— James Michael Hare,

Tôi đoán cách dễ nhất (có thể không phải là cách tốt nhất) là thêm một khoảng trắng ở đầu và cuối cụm từ tìm kiếm, ví dụ: để thay thế toàn bộ các từ, hãy tìm kiếm: "drown" để nó không thay thế những thứ như vậy như "chết đuối".
— jay_t55

Câu trả lời:


126

Regex là cách tiếp cận dễ dàng nhất:

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

Phần quan trọng của mẫu là \bký tự siêu, khớp trên các ranh giới từ. Nếu bạn cần nó để sử dụng không phân biệt chữ hoa chữ thường RegexOptions.IgnoreCase:

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

15
Trong trường hợp \blà speak regex cho ranh giới từ.
— Ra mắt

6
Giải pháp của bạn là tuyệt vời! Nếu tôi nhiều bài một fn wrapper với regex thoát: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

Dòng phải làstring pattern = "\\btest\\b";
— Valamas

24

Tôi đã tạo một hàm (xem bài đăng trên blog tại đây ) bao bọc biểu thức regex, được đề xuất bởi Ahmad Mageed

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

5
Hãy nhớ sử dụng Regex.Escape()trên wordToFindđể các ký tự đặc biệt được hiểu là các ký tự thông thường.
— CheeseSucker

@MichaelFreidgeim, Regex.Escape () tạo ra sự khác biệt rất lớn nếu wordToFind nhiều hơn là chữ số. Ví dụ: hãy thử tìm kiếm một từ khó nghe được che dấu, "!% @ # \". Nó sẽ không hoạt động như mong đợi.
— Jroonk

@Jroonk, bạn đang chào đón để chỉnh sửa bài viết, nếu nó cải thiện câu trả lời
— Michael Freidgeim

8

Như nhận xét của Sga, giải pháp regex không hoàn hảo. Và tôi đoán là không thân thiện với hiệu suất.

Đây là đóng góp của tôi:

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

... Với các trường hợp thử nghiệm:

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

1
@Alexis, Bạn nên đổi tên hàm thành ReplaceWhitespaceSeparatedSubstrings. Ngoài ra, vui lòng cung cấp nhận xét đầu ra dự kiến ​​"cho mỗi trường hợp thử nghiệm. Nếu bạn đã thực hiện bất kỳ so sánh hiệu suất nào với phương pháp tiếp cận regex, vui lòng chia sẻ chúng.
— Michael Freidgeim.

Chỉ cần chạy các trường hợp thử nghiệm để xem kết quả đầu ra.
— Alexis Pautrot

1
Đây không phải là 'khoảng trắng được phân tách' mà là 'bất kỳ ký tự nào không phải là chữ cái hoặc số' được phân tách. Không, tôi không so sánh về độ hoàn hảo.
— Alexis Pautrot

2
Tôi đã làm việc với nó và thấy một lỗi: a = "4.99"; Console.WriteLine (a.ReplaceWholeWord ("9", "8,99")); kết quả là 4,98,99. Trong bối cảnh này, điều này trông giống như một ví dụ ngớ ngẩn, nhưng nó minh họa một vấn đề mà tôi đang gặp phải trong một dự án thực tế.
— Walter Williams

6

Tôi chỉ muốn thêm ghi chú về mẫu regex cụ thể này (được sử dụng cả trong câu trả lời được chấp nhận và trong hàm ReplaceWholeWord ). Nó không hoạt động nếu những gì bạn đang cố gắng thay thế không phải là một từ .

Đây là một trường hợp thử nghiệm:

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

(sẵn sàng dùng thử mã: http://ideone.com/2Nt0A )

Điều này phải được xem xét đặc biệt nếu bạn đang dịch hàng loạt (giống như tôi đã làm cho một số công việc i18n).


Nó được mong đợi. "Doin '" không phải là một "toàn bộ từ". Bạn đang cố gắng thay thế "các chuỗi con được phân tách bằng khoảng trắng"
— Michael Freidgeim

1

Nếu bạn muốn xác định những ký tự nào tạo nên một từ, tức là "_" và "@"

bạn có thể sử dụng hàm (vb.net) của tôi:

 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

Kiểm tra

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

Kết quả

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

0

Tôi không thích Regex vì nó chậm. Chức năng của tôi nhanh hơn.

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

-2

Bạn có thể sử dụng 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();

7
Tôi không phải là chuyên gia về C #, nhưng làm thế nào replacesẽ không thay đổi testingthành textingnhư được hỏi trong câu hỏi?
— King Midas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.