0

I am a complete newbie when it comes to Regular Expressions, and was wondering if somebody could help me out. I'm not sure if using a regEx is the correct approach here, so please feel free to chime in if you have a better idea. (I will be looping thru many strings).

Basically, I'd like to find/replace on a string, wrapping the matches with {} and keeping the original case of the string.

Example:

Source: "The CAT sat on the mat."    
Find/Replace: "cat"    
Result: "The {CAT} sat on the mat."

I would like the find/replace to work on only the first occurance, and I also need to know whether the find/replace did indeed match or not.

I hope I've explained things clearly enough.

Thank you.

Noich
  • 14,631
  • 15
  • 62
  • 90
Trevor
  • 2,186
  • 3
  • 16
  • 18

3 Answers3

5
Regex theRegex = 
    new Regex("(" + Regex.Escape(FindReplace) + ")", RegexOptions.IgnoreCase);
theRegex.Replace(Source, "{$1}", 1);

If you want word boundary tolerance:

 Regex theRegex = 
     (@"([\W_])(" + Regex.Escape(FindReplace) + @")([\W_])", RegexOptions.IgnoreCase)
 theRegex.Replace(str, "$1{$2}$3", 1)
It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
1

If you will be looping through many strings, then perhaps Regex might not be the best idea - it's a great tool, but not the fastest.

Here's a sample code that would also work:

        var str = "The Cat ate a mouse";
        var search = "cat";
        var index = str.IndexOf(search, StringComparison.CurrentCultureIgnoreCase);
        if (index == -1)
          throw new Exception("String not found"); //or do something else in this case here
        var newStr = str.Substring(0, index) + "{" + str.Substring(index, search.Length) + "}" + str.Substring(index + search.Length);

EDIT:

As noted in the comments, the above code has some issues.

So I decided to try and find a way to make it work without using Regex. Don't get me wrong, I love Regex as much as the next guy. I did this mostly out of curiosity. ;)

Here's what I came upon:

public static class StringExtendsionsMethods
{
    public static int IndexOfUsingBoundary(this String s, String word)
    {
        var firstLetter = word[0].ToString();
        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.ToString().Equals(firstLetter, StringComparison.CurrentCultureIgnoreCase))
                if (!previousWasLetterOrDigit)
                    if (s.Substring(i, word.Length).Equals(word, StringComparison.CurrentCultureIgnoreCase))
                    {
                        wordFound = true;
                        bool wholeWordFound = true;
                        if (s.Length > i + word.Length)
                        {
                            if (Char.IsLetterOrDigit(s[i + word.Length]))
                                wholeWordFound = false;
                        }

                        if (wholeWordFound)
                            return i;

                        sb.Append(word);

                        i += word.Length;
                    }

            if (!wordFound)
            {
                previousWasLetterOrDigit = Char.IsLetterOrDigit(c);
                sb.Append(c);
                i++;
            }
        }

        return -1;
    }
}

But I can't take credit for this! I found this after some Googling here, on StackOverflow and then modified it. ;)

Use this method instead of the standard IndexOf in the above code.

Community
  • 1
  • 1
MBender
  • 5,395
  • 1
  • 42
  • 69
  • 2
    Yours is noticeably faster (if you do it a million times, 8x better speed), **but** it suffers from the clbutt problem. If you put in Class as str and ass as search, it would come out with Cl{ass}, instead of Class. – It'sNotALie. Aug 05 '13 at 08:44
  • I need to find/replace only on the first occurance of the match – Trevor Aug 05 '13 at 08:45
  • @It'sNotALie. Hmm, fair point! I'll see if I can't make it better. – MBender Aug 05 '13 at 08:46
  • I've found a solution - it's by no means short, but it should work as the OP requested, and should be faster than Regex. :P – MBender Aug 05 '13 at 09:24
  • There are ways to make the performance of the .Net regex engine extremely fast. Foremost is pre-compiled regex objects. Second of which is the expression itself, which can be sped up by using unicode values and cached. I can't help but feel that you gentlemen are colouring .net regex with a dirty brush. – Gusdor Aug 05 '13 at 09:40
  • @Gusdor I don't think neither of us are "anti-Regex". More likely if there are ways to speed things up, we don't know about them. I'm also guessing there's some extra complexity to what you're suggesting. None of this is wrong of course - but since the OPs question seemed initially too trivial in my eyes for Regex, I decided to go with a no-Regex approach. I forgot about the word-boundary problem however... >_ – MBender Aug 05 '13 at 10:54
  • No problem. I don't want readers to be put off regex by performance concerns. Particularly junior engineers. Regex can save a lot of pain and in a lot of scenarios it is _the_ tool. If you are reading, junior engineers, optimize after there is a problem! Not before. Also, "I don't think neither"; are you mocking me? :D – Gusdor Aug 05 '13 at 14:18
  • Either my non-native-English is showing, or you didn't catch that I meant me and It'sNotALie. (who, in the first comment, also agreed about the performance thing). – MBender Aug 05 '13 at 14:36
1

Try this:

class Program
{
    const string FindReplace = "cat";
    static void Main(string[] args)
    {
        var input = "The CAT sat on the mat as a cat.";
        var result = Regex
            .Replace(
            input,
            "(?<=.*)" + FindReplace + "(?=.*)",
            m =>
            {
                return "{" + m.Value.ToUpper() + "}";
            },
            RegexOptions.IgnoreCase);
        Console.WriteLine(result);
    }
}
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78