Use string.Replace to match whole words

2020-04-16 08:55发布

问题:

I'm using NET 2.0 and WinForms.

Currently, I need a code to replace a string with another one in a given text, but in the text it should only look for whole words. What I mean is:

string name = @"COUNTER = $40
CLOCK_COUNTER = $60";
name = name.Replace("COUNTER", "COUNT");

It should only replace the first instance of COUNTER with COUNT, because that's whole word. However, it seems string.Replace does not take whole word into consideration.

Please don't recommend regex. I have already tried it, and it's too slow for my needs. I need something very fast and efficient. How could I accomplish this?

回答1:

string input = @"COUNTER = $40
CLOCK_COUNTER = $60";

string name = Regex.Replace(input, @"\bCOUNTER\b", "COUNT");

\b marks word boundries.


The only alternative to Regex is to develop your own algorithm! Search for "COUNTER" and test the previous and following character for not being a word character.


EDIT:

Here is my solution as extension method:

public static class ReplaceWordNoRegex
{
    private static bool IsWordChar(char c)
    {
        return Char.IsLetterOrDigit(c) || c == '_';
    }

    public static string ReplaceFullWords(this string s, string oldWord, string newWord)
    {
        if (s == null) {
            return null;
        }
        int startIndex = 0;
        while (true) {
            int position = s.IndexOf(oldWord, startIndex);
            if (position == -1) {
                return s;
            }
            int indexAfter = position + oldWord.Length;
            if ((position == 0 || !IsWordChar(s[position - 1])) && (indexAfter == s.Length || !IsWordChar(s[indexAfter]))) {
                s = s.Substring(0, position) + newWord + s.Substring(indexAfter);
                startIndex = position + newWord.Length;
            } else {
                startIndex = position + oldWord.Length;
            }
        }
    }
}

EDIT #2: And here is a solution with StringBuilder.

public static string ReplaceFullWords(this string s, string oldWord, string newWord)
{
    if (s == null) {
        return null;
    }
    int startIndex = 0; // Where we start to search in s.
    int copyPos = 0; // Where we start to copy from s to sb.
    var sb = new StringBuilder();
    while (true) {
        int position = s.IndexOf(oldWord, startIndex);
        if (position == -1) {
            if (copyPos == 0) {
                return s;
            }
            if (s.Length > copyPos) { // Copy last chunk.
                sb.Append(s.Substring(copyPos, s.Length - copyPos));
            }
            return sb.ToString();
        }
        int indexAfter = position + oldWord.Length;
        if ((position == 0 || !IsWordChar(s[position - 1])) && (indexAfter == s.Length || !IsWordChar(s[indexAfter]))) {
            sb.Append(s.Substring(copyPos, position - copyPos)).Append(newWord);
            copyPos = position + oldWord.Length;
        }
        startIndex = position + oldWord.Length;
    }
}


回答2:

Small workaround:

string name = @"COUNTER = $40
CLOCK_COUNTER = $60";
name=" "+name;
name = name.Replace(" COUNTER ", " COUNT ");

Main idea that you have to mark the word you're going to replace with some sort of symbols that other words that you do not want to replace have not



回答3:

I think you cannot achieve that string replace any faster (I'm talking about developing time) than by RegExp

        string input = @"COUNTER = $40 CLOCK_COUNTER = $60";
        string pattern = @"\bCOUNTER\b";
        string replacement = "COUNT";
        var regex = new Regex(pattern,RegexOptions.Compiled);
        string result = regex.Replace(input, replacement);

Adding RegexOptions.Compiled makes it faster if you intend to reuse

-------------------UPDATE-----------------------------

i remembered about this article that may fit your needs:

http://www.codeproject.com/KB/string/fastestcscaseinsstringrep.aspx