user3267755
user3267755

Reputation: 1070

Highlight search terms in a string

I have written a function which works with my sites search functionality. When the user searches a word, I perform a replace on the returned search content to take any word that the user entered into the search, and wrap it in span tags with a custom class which will basically bold the word on the page. After overcoming my first road block of having to incorporate case-insensitive replacements, I'm now stuck in another predicament. The word that is replaced on the page is being replaced with the users provided case on the page which looks funny because the content returned is a lot of legal text and acronyms. If a user were to search "rpC 178", the "RPC 178" in the content is displayed as bold and the same case "rpC 178". My first thought was to split the content by "space" and keep a temporary copy of the replaced word before it's replaced in order to preserve it's current case but some of these content blocks can be upwards of 4000 words so that seems inefficient. Am I going about this the wrong way?

Here is my current code:

public static String HighlightWords(String content, String className, String searchTerms)
{
    string[] terms = new string[] { };
    if (!string.IsNullOrWhiteSpace(searchTerms))
    {
        terms = searchTerms.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
    }
    if (terms == null || terms.Length == 0)
    {
        return content;
    }

    var optimizedContent = new StringBuilder(content);
    var startHtml = string.Format("<span class=\"{0}\">", className);
    var endHtml = "</span>";
    string result = string.Empty;
    foreach (var term in terms)
    {
        result = Regex.Replace(optimizedContent.ToString(), term, string.Format("{0}" + term + "{1}", startHtml, endHtml), RegexOptions.Compiled | RegexOptions.IgnoreCase);
    }

    return result;
}

Upvotes: 4

Views: 2694

Answers (1)

NineBerry
NineBerry

Reputation: 28499

You can use the other overload of the Regex.Replace method that accepts a MatchEvaluator delegate. Here you pass a method that gets the actual text found as a parameter and can dynamically build the string to use as a replacement.

Sample:

    string output = Regex.Replace(input, term, 
        match => startHtml + match.Value + endHtml, 
        RegexOptions.Compiled | RegexOptions.IgnoreCase);

Note that the notation with the => symbol may not work with older versions of C#. In this case you have to use the longer form:

    string output = Regex.Replace(input, term, new MatchEvaluator(match => 
         {
             return startHtml + match.Value + endHtml;
         }), 
         RegexOptions.Compiled | RegexOptions.IgnoreCase);

So you can also improve your code because you do not need a foreach loop over all the specified search terms. Just build a regular expression that contains all the terms to look for and then use that for searching.

Remember to use Regex.Escape() to escape the data entered by the user before using it for searching with the Regex class, so that everything works as expected when the user enters characters that have a special meaning in regular expressions.

Upvotes: 7

Related Questions