Use regular expressions in Visual Studio to find (and replace) anything

Use regular expressions in Visual Studio to find (and replace) anything

This is one of those things you realize after long time using the tool

It doesn’t matter what you do. If you are writing any sort of code, you will eventually use the Find/Replace functionality (also known as Ctrl+H or Ctrl+Shift+H). The most basic situation is to, for example, make all public methods into protected. But what if you would want to invert the values of a ternary condition or change the order of parameters in multiple methods?

Regular expressions to the rescue.

Yes, I know. You normally use regular expressions in your code when trying to find something. But bear with me.

What are Regular Expressions?

If you are not familiar, a regular expression (or regex) is a sequence of characters that specifies a search pattern. A regex can be used for anything: Find, find/replace, or even input validation. For example:

Regex Explanation
gr(a|e)y Matches 'gray' or 'grey'
\d{5}(-\d{4})? Matches US zip codes
//[^\r\n]*[\r\n] Matches a comment starting with slash-slash (C# or Java)

 

As you can see, they are not very friendly and, honestly, there is a very low number of people who know them by heart. If you want to create your own regex I’d recommend to use websites like Regex101, which can also help you to test it.

Find with Regex

Look at the method below:

public override void Process(TagHelperContext context, TagHelperOutput output)
{
    if (Items == null)
    {
        return;
    }

    output.TagName = "div";
    output.Attributes.SetAttribute("class", OuterContainerClass);

    InnerContainerClass = !string.IsNullOrWhiteSpace(InnerContainerClass) ? $" class=\"{InnerContainerClass}\"" : "";
    LabelClass = !string.IsNullOrWhiteSpace(LabelClass) ? $" class=\"{LabelClass}\"" : "";
    InputClass = !string.IsNullOrWhiteSpace(InputClass) ? $" class=\"{InputClass}\"" : "";

    string isChecked;
    string isEnabled;
    var sb = new StringBuilder();

    foreach (var item in Items)
    {
        isChecked = item.Selected ? " checked" : "";
        isEnabled = item.Enabled ? "" : " disabled";
        sb.Append("<div").Append(InnerContainerClass).Append(isEnabled).Append('>');
        sb.Append("<label").Append(LabelClass).Append('>');
        sb.Append("<input type=\"checkbox\" id=\"").Append(item.Value).Append("\" value=\"").Append(item.Value).Append("\" name=\"")
            .Append(Name).Append('"').Append(InputClass).Append(isChecked).Append('>').Append(item.Label);
        sb.Append("</label></div>");
    }

    output.Content.SetHtmlContent(sb.ToString());
    output.TagMode = TagMode.StartTagAndEndTag;
}

If you want to find LabelClass you will simply do a Ctrl+F and type the text in there. Like this:

But how will you find every occurrence of any line which has class=\"{***Class}, with the asterisks being any text (Input, Label, etc.)? For that we will have to use regex, like in the example below:

Attention to the red arrows. The one on the bottom indicates the option we should select in order to use regex. The other one, on the left of the search box, shows the regex I used: class=\\"{.*Class}. I used the string I was looking for with 2 minor differences: First, I needed to escape the backslash. And second, I needed to add the .* before Class. In regex, the . (period) will match any character while the * (asterisk) indicates repetition. Together they mean something like “match any character for an infinite number of times”. And if I want to find only letters, I should change to this:

The regex class=\\"{[a-zA-Z]*Class} means something like “match the characters in the range a-z and A-Z”. We must specify both upper and lower cases since regex is case-sensitive.

Replace with Regex

The only difference here is the shortcut key used to open the window (Ctrl+H). Once we found the text we want to replace, we simply press Alt+R (replace next) or Alt+A (replace all):

Done, but that’s not all. What if you want to invert the values in those ternary conditionals all at once? For that we need to use capturing groups.

Capturing groups are pieces of regex inside parenthesis, like this: (.*)([a-z]). In this basic example we have 2 different capturing groups in the “find”, which can be referenced in the “replace” by the dollar sign ($) followed by a number (1, 2, etc.). For example: $1. Important to mention that capturing groups are 1-based indexing, which brings us to our example:

Before

As you can see, we have 2 groups, each one of them matching an item in the conditional.

After

And here you see the result, by simply building the “replace” with the inverted groups.

Using in other editors

It is possible to use regex in other places too, such as Notepad++ and Visual Studio Code. However you have to consider the "flavor" of the regex. The core idea is the same, but there may be a couple of minor differences which will be enough to drive anyone crazy.

Check Devopedia for more information on regex.