A Comprehensive Guide to Regular Expressions
Tags: regex, javascript, programming
Published: 2022-08-03 10:11:00


When I first saw regular expressions, I thought they looked like something nonsensical that a toddler had scribbled down on a piece of paper. It looked like gibberish, gobbledygook, a bunch of symbols and characters that someone had slapped together. Take a look at this:


```js

/^\w+@\w+\.\w+(\.\w+)?$/

```

This is a regular expression for validating email addresses. Told you, gibberish looking. Don't worry if you don't understand what this means right now, we'll go through a series of examples and discuss how to write regular expressions. Before we dive into how to write them, let's define them first.

## What are Regular Expressions

I think of regular expressions(*also known as regex*) as a **test for matching string patterns**. A test because we are checking whether a given text input, contains a particular pattern. Regex comes in six different dialects and regardless of what programming language you use, you're implementing one of the following dialects:

|Flavor |Description | 
| :---|---:|
|**BRE** | Basic Regular Expressions|
|**ERE** | Extended Regular Expressions|
|**EMACS** | EMACS Implementation|
|**VIM** | VIMs Implementation|
|**PEARL** | Perl Six, Perl Seven, Perl Eight|
|**PCRE** | Perl Compatible Regular Expressions|
 

>  :bulb:  "The examples I'll be giving will be in JavaScript, which uses a PCRE derivative syntax"


The **regex engine** is the thing that's responsible for performing the matching that happens every time you perform a regular expression operation. You don't need to know the nitty-gritties of how it actually works under the hood, but it's important to mention it. If you're interested in reading more about it, head on over to [Devopedia](https://devopedia.org/regex-engines)

So now that we know that a regex is a text pattern, how do we actually write them?

## Writing Regular Expressions.
You can create regular expressions in one of two ways in JavaScript:
### Using the built-in RegExp Constructor
```js

let regex = new RegExp("Bruce");

```  

or

### Using the literal notation
```js

let regex = /Wayne/;

```  


> :bulb: The literal syntax is preferred when the regex is known at development time, and the constructor approach is used when the regex is constructed at runtime by building it up dynamically in a string.
***-Secrets of the JavaScript Ninja, Second Edition***

> We'll be using the literal notation from here on out.

We defined regular expressions as patterns, so where is the pattern. From our example above, "Wayne" is our pattern. We have a "W" followed by the letter "a" followed by "y" and so forth. This is a pattern because only by combining those letters, will you get the word "Wayne".

So now that we have a pattern, how do we actually test it against something else like another word or a sentence? 

### Testing, Matching, and Extracting patterns.

```js

let regexPattern = /Wayne/;
let textInput = "It's not who I am underneath, but what I do that defines me. - Bruce Wayne";

```
We want to check whether our regex pattern exists in the text input. JavaScript provides a number of methods that we can use.

 
|Method |Description | 
| :---|---:|
|**test** | Returns true if there was a match, or false if no match was found|
|**match** | Returns an array if there was a match or null if no match was found|
|**exec** | Returns an array if there was a match or null if no match was found|
|**search** | Returns the index position of the match or -1 if no match was found|
|**matchAll** | Returns an iterator of all results matching or -1 if no match was found|
|**replace** | Returns a new string with some or all matches replaced by a replacement string|
|**replaceAll** | Returns a new string with all matches of a pattern replaced by a replacement string|


We'll have a look at the first three methods and see how they work. If you're interested in learning more about how the methods work, you can head on over to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) and do a deep dive into them.

Let's first try to match our pattern using the test method. 
```js

let regexPattern = /Wayne/;
let textInput = "It's not who I am underneath, but what I do that defines me. - Bruce Wayne";
console.log(regexPattern.test(textInput)) // Returns true

```
The test method is called on the regex itself and it takes in a single argument, the text input. It's particularly useful when you simply want to see whether the pattern exists or not and are not concerned with extracting anything after matching.

The match method gives you a bit more information regarding the match.

```js

let regexPattern = /Wayne/;
let textInput = "It's not who I am underneath, but what I do that defines me. - Bruce Wayne";
console.log(textInput.match(regexPattern))

```

```js

[
'Wayne',
index: 69,
input: "It's not who I am underneath, but what I do that defines me. - Bruce Wayne",
groups: undefined
]

```
As you can see, we get an array that has information pertaining to the match. We get the pattern that matched, we also get the index position of the match as well as the input text that was passed in. We'll talk about groups in the coming section. **The method is called on the text input and not on the regex itself.** 

The exec method also returns an array: 

```js

let regexPattern = /Wayne/;
let textInput = "It's not who I am underneath, but what I do that defines me. - Bruce Wayne";
console.log(regexPattern.exec(textInput)) // Returns an array"

```

```js

[
'Wayne',
index: 69,
input: "It's not who I am underneath, but what I do that defines me. - Bruce Wayne",
groups: undefined
]

```
As you can see, the output is identical to that of the match method, however, the exec method is called on the regex itself and not on the text input as we saw in the match method.


Now that we've seen how to test and extract matches, we can now begin to dissect the actual matching and try to see how the regex engine works at a lower level. This will hopefully have you thinking about your regex patterns differently even as you run your operations.

### How does the matching occur.

We talked about the regex engine a bit at the beginning of the post, now let's see how it runs every time we try to match a pattern. 
The regex engine creates a graph that contains all the characters that need to match. In our case, we are trying to match the pattern "Wayne". The input text is the input we are trying to match against in this case, _"It's not who I am underneath, but what I do that defines me. - Bruce Wayne"_


![Regex engine graph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m1xu238pcza7vydclp9.png)

As you can see, our pattern has been split up into individual letters, that the regex engine will use to determine whether we match or not. **The regex engine will always try to match the leftmost match first**. So in this case, it will try to match "W" before moving along the graph to the next letter.

The regex engine will go through each word, trying to match the "W" and won't move on to the next letter until it has tried to match all possible paths. If it fails, then the regex engine will fail to match and stop trying. 

So in our case, we will encounter the "W" at the very end of our string. Once we match it, the regex engine will move on to the next letter in the pattern until it matches the entire pattern. 


Here are a few key takeaways regarding the regex engine:


- The engine tries every possible execution path before it moves along the string
- The regex engine will find the earliest possible match and not the best possible match.
- The regex engine will always try the leftmost match first
- The regex engine always matches as early as possible in the string on the leftmost possible alternative in the regex.
- The regex engine also returns as soon as it finds a match.

> :bulb: Write regular expressions that match. This will improve the performance of the regex as it won't have to traverse through all the possible matches.

Up to this point, we've been matching basic patterns that don't have things like alternative matching, matching at specific positions and so fourth. That's what we are going to be looking a next.

#### Flags

Flags are used to add flexibility in terms of what is considered a match. For example the **i** flag is used to make the regex engine match regardless of case. So "Wayne" and "WAYNE" would both match.

The table below describes the flags that are available to us:


|Flag |Description | 
| :---|---:|
|i|ignores case|
|g|Perform a global search and don't return only after the first match|
|m| When the multi-line flag is enabled, beginning and end anchors (^ and $) will match the start and end of a line, instead of the start and end of the whole string.|
|y|The expression will only match from its lastIndex position and ignores the global (g) flag if set|
|u|When the unicode flag is enabled, you can use extended unicode escapes in the form \x{FFFFF}.|
|s|Dot (.) will match any character, including newline.|


We'll have a look at the two most common, flags the **i** flag and the **g** flag, but if you're interested in learning more about flags you can visit the [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#advanced_searching_with_flags) documentation.


The **i** flag is used to match a pattern regardless of the case.


```js

let regexPattern = /Wayne/i; // You add the flag after the closing forward slash.
let textInput = "It's not who I am underneath, but what I do that defines me. - Bruce WAYNE";
console.log(regexPattern.test(textInput)) // Returns true
  
```

The **g** flag tells the regex engine, keep looking for more matches even after you've encountered the first match.


```js

let regexPattern = /Wayne/ig; // You can combine flags to add more flexibility to your regex.

// ['Wayne','Wayne']

```


#### Match any character
Sometimes we want to match any character in our pattern from digits to numbers to symbols to white-spaces.

Writing this all out explicitly will be too verbose and clunky, so we use the **period** symbol which matches any character.

```js
let matchAnything = /./
```


#### Grouping
Grouping is used to group patterns together to make it easier to not only read our patterns but to also do more intuitive things as we match our patterns. We use parenthesis to group our patterns.

```js
 let groupedPattern /(nickle)(back)/
```



#### Match patterns at specific positions

So far we've been matching patterns at any position but what if we want to match at the start or at the end of a string?

|Symbol|Description|
|:---|---:|
|**^**| Match a pattern at the start |
|**$**|Match a pattern at the end|


```js
  let matchStart = /^a/ // Match an "a" at the beginning. 
  let matchEnd = /a$/ // Match an "a" at the end.
```
The `^` can also be used to negate characters. To negate a character, we use `[^character]` syntax. For example if we wanted out pattern to not include an "a" we would write:

```js
let notIncludingA = /[^a]/
```


#### Quantifiers

Quantifiers tell the regex engine how many times to match a character or pattern. 

|Quantifier |Description | 
| :---|---:|
|*|Match a pattern zero or more times|
|**+**|Match a pattern one or more times|
|**?**|Match a pattern zero or once|
|**{x}**|Match a pattern an arbitrary number of times|


```js
let matchZeroOrMore = /a*/
let matchOneOrMore = /a+/
let matchZeroOrOnce = /a?/
let matchArbitraryTimes = /a{3}/ // Match three times

```

The quantifier is placed after the character you are trying to quantify. the **?** is used to match an optional character as in the example below:

```js
let pattern = /favou?rite/
// This will match favourite or favorite
```
You can also use the `|` symbol to match one or more options. So we can rewrite the above patterns as `/favourite|favorite/`and this would still work.

Sometimes you may want to match a set of characters in a single position. To do this, we use the character set syntax which is denoted using **[]**. 

```js
let pattern = /gr[ae]y/
```

This will now match either an "a" or an "e" in that position. We can include a range of numbers or letters to add more flexibility to the pattern. 

```js
  let pattern = /gr[a-f]y/
  console.log(pattern.test("His grey hair is starting to show")); // True
```

#### Shorthand syntax
The shorthand syntax is used to match a variety of characters without having to explicitly define the characters themselves.
 
For example lets assume we want to match all letters and numbers in a single position. If you were to write this explicitly, you'd write `[a-z0-9]`. 

This feels and looks a bit verbose, so instead, we can use the shorthand symbol `\w`. 

|Symbol|Description|
|:--|--:|
|**\w**|Match alphanumeric characters`[a-zA-Z0-0_]`.|
|**\d**|Match digits `[0-9]`|
|**\s**|Match white-spaces `[\t\r\n\f]`|
|**\W**|Match anything that is **not** an alphanumeric character `[^0-9A-Za-z_]`|
|**\D**|Match anything that is **not** a digit `[^0-9]`|
|**\S**|Match anything that is **not** a white space|


```js
let alphaNumericsMatch = /\w/
let digitsMatch = /\d/
let whiteSpaceMatch = /\s/
let nonAlphaNumerics = /\W/
let nonDigits = /\D/
let nonWhiteSpace = /\S/
```


We've covered a lot of material. From defining regular expressions, to writing them, to using them to match and extract patterns. 

This will hopefully serve as a guide as you navigate your way through regular expressions.

#### Resources

[Regex101](https://regex101.com/)
[RegexOne](https://regexone.com/)

Thanks for reading and happy coding!! :boom: