Published on

RegEx Find and Replace

Authors

In my day to day work as a developer, I sometimes run into situations where I have data I want to use but that requires some manipulation before it can be useful. To solve this I may use a simple find replace to achieve what I want but oftentimes this is not enough - enter the Regular Expression (RegEx) find replace.

To illustrate say I have a simple Java bean called Person as in the below example:

public class Person {

    private String name ="";
    private String surname = "";
    private String title = "";
    private int age = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Now I have intialized the fields in this bean so that their default values are not null. The issue I have is that I want to prevent these fields from being changed to null when they are set to other values.

I could go to each setter and add an If statement to only assign none-null values and otherwise do nothing as in the example below:

public void setName(String name) {
    if (name != null) {
        this.name = name;
    }
}

This was fairly simple to do by hand but doing this for a large bean becomes tedious and error prone. So to get around this I will first define a method to handle the checking and conditional assigning of a value, it will have to take generic values as my bean has different data types so I would add something like:

private <T> T notNullOrDefault(T potentiallyNullValueToCheck, T noneNullDefault) {
  if (potentiallyNullValueToCheck == null) {
    return noneNullDefault;
  }
  return potentiallyNullValueToCheck;
}

What this method does is take two parameters of the same type, if the first parameter is null the second parameter is returned otherwise the first parameter is returned.

Now the problem is if I use a standard find/replace I cannot easily wrap this method around the setters as each field has a different name. So to get around this I use the RegEx find replace feature in IntelliJ/Android Studio.

Note: this should also be available in other popular editors like Atom, Sublime and Vim but I will demonstrate this in IntelliJ/Android Studio as that is what I am most comfortable with. The main area where your mileage may vary is the syntax you use to refer to different matching groups (you will need to do a bit of googling for your specific editor).

Before I continue I will reformat the spacing in the file to ensure that all equals symbols are equal spacing from the assignments in the setters by pushing ctrl/cmd+alt+L to reformat the code. Then I will push ctrl/cmd+r to get the replace bar as in the screenshot below:

RegEx Find replace bar Android Studio and IntelliJ

Note how the "Regex" option is checked.

The find RegEx Explained

Now to match this we need to learn a bit about regular expressions, I will show you the full expression we will use in the find box and I will break down what each component means:

(this\.\w+) \= (\w+);
  1. (this\.\w+)

  2. this: will literally match the word this as in the this in this.name for example

  3. \.: a . in regular expressions has a special meaning so to escape it I use a back slash \

  4. \w+: \w will match any single word character i.e any letters a-z or A-Z or number 0-9. The + symbol in regex means at least one ore more so a would match and so would ab but a= would not since = is not a word character

  5. ( and ): In regex this is called a matching group. You wrap brackets around the expression you are trying to match. In our case this will be something like this.name where we match it as the first match.

  6. \=: will simply match the = symbol, again we need to escape it as = has special significance in RegEx.

  7. (\w+);: this will match the bit after the = so for example with this.name = name we match name as it appears after the equals.

A really useful place to play around with your Regex and get it right is: https://regex101.com/r/kZ2lW6/2

I have put this example RegEx there to try out as in the screenshot below:

RegEx on regex101.com

To make 100% sure this is working we will first place this regex in the find box in IntelliJ/ Android Studio by pushing ctrl/cmd+f and ensuring that the "Regex" checkbox is checked. If all is good so far then the insides of all the setters should be highlighted as below:

Our setters highlighted in the find

Now that we are happy we will push cmd/ctrl+r to get the replace bar and input the following:

  1. The first search box will have our regex from earlier: (this\.\w+) \= (\w+);
  2. The second box will have this: $1 = notNullOrDefault($2, $1);
Regex find replace with replacement string

The IDE will also give me an awesome hint to show that we are on the right track as below:

IDE find replace hint

The second regex explained

$1 = notNullOrDefault($2, $1);

  1. $1: This can differ depending on the editor you are using but in Jetbrains IDEs this refers to the first matching group so it would be the thing in-between the first set of ( and ) so for example (this.name) = name would pick up this.name
  2. = notNullOrDefault(: This is just a plain old equals followed by our function name and an opening bracket
  3. $2: This will take our second matching group, the thing between the second pair of brackets so this.name = (name); would give name
  4. , $1);: this is simply a comma followed by the first match and a closing bracket with a semi colon.

This regex works since we know that our first match refers to the current field in the class (e.g. this.surname) which is initially initialized with a none-null value and is prevented from ever being null using this function which makes it a valid null default in our method.

I am happy with the expression so I hit the Replace All button and voila as in the gif and code sample below I have my setters only setting none-null values to this bean's fields.

RegEx find replace in action

The final code is as below:

public class Person {

    private String name = "";
    private String surname = "";
    private String title = "";
    private int age = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = notNullOrDefault(name, this.name);
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = notNullOrDefault(surname, this.surname);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = notNullOrDefault(title, this.title);
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = notNullOrDefault(age, this.age);
    }

    private <T> T notNullOrDefault(T potentiallyNullValueToCheck, T noneNullDefault) {
        if (potentiallyNullValueToCheck == null) {
            return noneNullDefault;
        }
        return potentiallyNullValueToCheck;
    }
}

As you can see the regular expression find replace can be incredibly powerful and save you plenty of manual error prone work. Regular expressions can be tricky to grasp at first but once you get a handle on them and play around with them using a site like https://regex101.com there is plenty more you can do.