【问题标题】:Way to have String.Replace only hit "whole words"让 String.Replace 只打“整个单词”的方法
【发布时间】:2011-09-02 20:56:22
【问题描述】:

我需要一个方法来实现这个:

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

返回这个:

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

基本上我想替换整个单词,但不是部分匹配。

注意:我将不得不为此使用 VB(SSRS 2008 代码),但 C# 是我的常用语言,因此任何一种都可以。

【问题讨论】:

  • 我想最简单的方法(可能不是最好的方法)是在搜索词的开头和结尾添加一个空格,例如替换整个单词,搜索:“淹死”所以它不会取代诸如“溺水”之类的东西。

标签: c# .net vb.net string


【解决方案1】:

正则表达式是最简单的方法:

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

该模式的重要部分是\b 元字符,它匹配单词边界。如果您需要它不区分大小写,请使用RegexOptions.IgnoreCase:

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

【讨论】:

  • \b 是正则表达式代表单词边界的地方。
  • 您的解决方案很棒!如果我发布了一个带有正则表达式转义的 fn 包装器:static string ReplaceFullWords( string input, string from, string to) { if (input == null) { return null; } return Regex.Replace(input, "\\b" + Regex.Escape(from) + "\\b", to); }
  • 该行应该是string pattern = "\\btest\\b";
【解决方案2】:

我创建了一个包装正则表达式的函数(参见blog post here),由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;
}

【讨论】:

  • 记得在wordToFind 上使用Regex.Escape(),这样特殊字符就会被解释为常规字符。
  • @MichaelFreidgeim,如果 wordToFind 不仅仅是字母数字,Regex.Escape() 会产生巨大的差异。例如,尝试搜索带掩码的脏话“!%@#\”。它只是无法按预期工作。
  • @Jroonk ,如果它改善了答案,欢迎您编辑帖子
【解决方案3】:

我只想添加有关此特定正则表达式模式的注释(在接受的答案和 ReplaceWholeWord 函数中都使用)。 如果您要替换的不是单词,则它不起作用。

这里是一个测试用例:

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

(准备试用代码:http://ideone.com/2Nt0A

必须考虑这一点,尤其是在您进行批量翻译时(就像我在一些 i18n 工作中所做的那样)。

【讨论】:

  • 这是预期的。 “做”不是一个“完整的词”。您正在尝试替换“空格分隔的子字符串”
【解决方案4】:

正如 Sga 所评论的,正则表达式解决方案并不完美。而且我猜对性能也不友好。

这是我的贡献:

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

... 带测试用例:

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,您应该将该函数重命名为 ReplaceWhitespaceSeparatedSubstrings。还请为每个测试用例提供预期的输出”评论。如果您对正则表达式方法进行了任何性能比较,请分享。
  • 只需运行测试用例即可查看输出结果。
  • 这不是“空格分隔”而是“任何字符不是字母或数字”分隔。不,我没有进行性能比较。
  • 我一直在使用它,发现一个失败:a = "4.99"; Console.WriteLine(a.ReplaceWholeWord("9", "8.99"));结果为 4.98.99。在这种情况下,这看起来像是一个愚蠢的例子,但它说明了我在实际项目中遇到的问题。
【解决方案5】:

你可以使用 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();

【讨论】:

  • 我不是 C# 方面的专家,但是replace 不会像问题中所问的那样将testing 更改为texting
【解决方案6】:

如果你想定义一个单词由哪些字符组成,即“_”和“@”

你可以使用我的 (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

测试

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

结果

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

【讨论】:

    【解决方案7】:

    我不喜欢正则表达式,因为它很慢。我的功能更快。

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

    【讨论】:

      【解决方案8】:

      如果你有兴趣,这个方法也会忽略大小写

      public static string Replace(this string s, string word, string by, StringComparison stringComparison, bool WholeWord)
      {
          s = s + " ";
          int wordSt;
          StringBuilder sb = new StringBuilder();
          while (s.IndexOf(word, stringComparison) > -1)
          {
              wordSt = s.IndexOf(word, stringComparison);
              if (!WholeWord || ((wordSt == 0 || !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt - 1, 1)))) && !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt + word.Length, 1)))))
              {
                  sb.Append(s.Substring(0, wordSt) + by);
              }
              else
              {
                  sb.Append(s.Substring(0, wordSt + word.Length));
              }
              s = s.Substring(wordSt + word.Length);
          }
          sb.Append(s);
          return sb.ToString().Substring(0, sb.Length - 1);
      }
      

      【讨论】:

        猜你喜欢
        • 2012-01-26
        • 2014-05-18
        • 2020-04-06
        • 2014-12-14
        • 1970-01-01
        • 2019-12-30
        • 2023-04-01
        • 1970-01-01
        • 2018-04-29
        相关资源
        最近更新 更多