Introduction to Regex
Yohanes Setiawan
Posted on April 24, 2022
Have you ever created a password, and there we were asked to use capital letters, numbers, and symbols? And if so, how could we do it? Are we gonna split the input string, and then check for each character if all the criteria is matched? Let's see the implementation below for validating password.
function validatePassword(input) {
// list of special character
let specialChars = ['!','@','#','$','%','^','&','*','(',')','_','+','-','=','[',']','{','}',';','~','?','/','<','>',',','.','|','\\', ':'];
const validLength = input.length > 6 ? true : false;
let validCapital = false;
let validNumber = false;
let validSpecialChar = false;
const isSpecialCharacter = (char => specialChars.includes(char));
const isCapitalcase = (char => char.toUpperCase() === char);
const isNumber = (char => [1,2,3,4,5,6,7,8,9,0].includes(parseInt(char)))
const _input = input.split('');
_input.forEach(_input => {
if(isNumber(_input)) validNumber = true;
if(isSpecialCharacter(_input)) validSpecialChar = true;
if(!isNumber(_input) && !isSpecialCharacter(_input) && isCapitalcase(_input)) validCapital = true;
});
if (validLength && validCapital && validNumber && validSpecialChar) {
return true;
} else {
console.log('error creating password!');
return false;
}
}
It's complex, right? Lo and behold the power of regex
function validatePasswordRegex(input) {
return /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/.test(input);
}
Our code will become so much clearer in an instant โจ. (and in so mysterious way...)
Worry not! I will give you an introduction about regex in this article.
Regex stands for Regular Expression and it is basically an easy way to define a pattern of text. Regex is often used for input validation or text identification.
At first glance, Regex is like a gibberish text. However for those who know how to use it, it will be one of powerful tools in their disposal ๐ .
I have listed some commonly used tokens in Regex to help us. ๐
Tokens | Represent |
---|---|
\d | Any Digit |
\D | Any Non-digit character |
. | Any Character |
\. | Period |
[abc] | Only a, b, or c |
[^abc] | Not a, b, nor c |
[a-z] | Characters a to z |
[0-9] | Numbers 0 to 9 |
\w | Any Alphanumeric character |
\W | Any Non-alphanumeric character |
{m} | m Repetitions |
{m,n} | m to n Repetitions |
* | Zero or more repetitions |
+ | One or more repetitions |
? | Optional character |
\s | Any Whitespace |
\S | Any Non-whitespace character |
^โฆ$ | Starts and ends |
(โฆ) | Capture Group |
(a(bc)) | Capture Sub-group |
(.*) | Capture all |
(abc|def) | Matches abc or def |
First thing to note, when we are using regex everything is a character and we are writing patterns to match a specific sequence (pattern).
// alphabet abc
/abc/.test('abcd'); // match โ
/abc/.test('abc'); // match โ
/abc/.test('abcde'); // match โ
Look at the example above, abc
is our pattern, we are done our first regex ๐. It's as simple as the common letters on each line.
// number 123
/123/.test('bgh123'); // match โ
/123/.test('const number = 123'); // match โ
/123/.test('1 + 2 + 123 = 125'); // match โ
We can see 123
(which is number) is also treated as character in regex.
// dot .
/...\./.test('asd.a'); // match โ
/...\./.test('12345'); // skip โ
/...\./.test('123.s'); // match โ
.
dot operator in regex is also known as a wildcard because it can match any character. However in the example above we can use \.
to actually specify the .
("dot") to be in our pattern.
// brackets ['asdf']
/[FfR]oot/.test('Foot'); // match โ
/[FfR]oot/.test('Root'); // match โ
/[FfR]oot/.test('foot'); // match โ
/[FfR]oot/.test('Moot'); // skip โ
While the .
metacharacter is pretty powerful to match all the character, we need to somehow add a boundary to match to specific characters. We could do that by using []
brackets notation. In our example we use [FfR]
as our pattern, therefore only F, f or R letter will be match and nothing else.
// inverse within brackets [^]
/[^FfR]oot/.test('Moot'); // match โ
/[^FfR]oot/.test('Boot'); // match โ
/[^FfR]oot/.test('Foot'); // skip โ
We also could use an [^]
"inverse within brackets" to excluding specific characters, if there is any character in our test that are same as the characters inside our brackets it will be not match.
// Character ranges
/[A-D][c-h][b-e]/.test('Ade'); // match โ
/[A-D][c-h][b-e]/.test('Dfd'); // match โ
/[A-D][c-h][b-e]/.test('Erd'); // skip โ
We can specify a "range" of sequential characters by using the -
"dash" within []
"brackets" to indicate a character range. We could specify a digit [1-7]
, or even with inverse notation [^a-f]
. Notice that a-z
"lowercase" and A-Z
"uppercase" are different, so make sure we put it both if we want all the alphabets to be match. [A-Za-z0-9_]
this pattern is equivalent for \w
which stands for match for any alphanumeric characters. And \W
can be used to specify non-alphanumeric characters.
// Curly brace repetitions
/hel{3}o/.test('helllo'); // match โ
/hel{3}o/.test('hello'); // skip โ
/hel{3,7}o/.test('hellllllo'); // match โ
/hel{3,7}o/.test('hello'); // skip โ
We can speficy a number of repetitions for a character we want to match by using {a, b}
where a
is a minimal number of repetitions, and b
is maximum number of repetitions. If b
is not specified, then the repetitions must be exactly same as a
. In our example we specify hel{3}o
, which means l
character must be repeated 3 times to be match, otherwise it will be not match. hel{3,7}o
means l
character must be repeated between 3 until 7 to be match.
// Plus (+) and star (*) quantifier
/o+l*m+/.test('ooolllmm'); // match โ
/o+l*m+/.test('ooomm'); // match โ
/o+l*m+/.test('mmm'); // skip โ
/o*l*m+/.test('mmm'); // match โ
How can we match the character to be at least 1 or more character that it follows? Meet the +
"plus" quantifier. There are also *
star quantifier, which can be used to represents either 0 or more of the character that it follows.
// Question Mark (?) optional
/\d+ files? is found/.test('1 file is found ๐พ'); // match โ
/\d+ files? is found/.test('4 files is found ๐พ'); // match โ
/\d+ files? is found/.test('No files is found ๐พ'); // skip โ
In the example above, we could give our pattern more flexibility by specify an optional character using ?
"question mark" into our patterns. In our example we using ?
to specifying wheter our statement is singular or plural. "1 file found" (singular) and "4 files is found" (plural), and both is matching โ
.
// Whitespaces
/white\s+space/.test('white space'); // match โ
/white\s+space/.test('white space'); // match โ
/white\s+space/.test('whitespace'); // skip โ
/white\S+space/.test('whites space'); // skip โ
/white\S+space/.test('whites_space'); // match โ
Do you know that space, tab, newline, carriage return, form feed and vertical tab characters are called "whitespace characters"? We could use \s
to specify a "whitespace" in our pattern. There are also \S
(with capital "S") to specify "non-whitespace" character (e.g: alphanumeric and special characters).
// Hat (^) and dollar sign ($) - Start ... End
/^Start your day with orange ๐$/.test('Start your day with an orange ๐'); // match โ
/^Start your day with/.test('Start your day with an apple ๐'); // match โ
/with orange ๐$/.test('Finish your day with an orange ๐'); // match โ
/^Start your day with ๐/.test(' Start your day with an orange ๐'); // skip โ
/^Start your day with orange ๐$/.test(' Start your day with an orange ๐ '); // skip โ
So far we are matching the text regardless the position of the character, whether it is on start, middle or near the end of the text. How could we make a boundaries to search the text that must be start in ... and end with ... In that case, we use ^
"hat" and $
to match a pattern. In our example above Start your day with orange ๐
we see that the pattern that didn't start with ^
is can be match regardless of whitespace or even incorrect sentence, it is also same with the pattern that didn't end with $
. One last note [^]
(inversion) is different than ^
(start with).
// Matching Group (...)
('js_basic.txt').match(/^(js_.+)\.txt$/); // capture: "js_basic" โ
('js_intermediate.txt').match(/^(js_.+)\.txt$/); // capture: "js_intermediate" โ
('js_101.xls').match(/^(js_.+)\.txt$/); // capture: null โ
('python.txt').match(/^(js_.+)\.txt$/); // capture: null โ
Is it possible to extract data from our matching text so we can use that? ๐๐ปโโ๏ธ Yes it is! We can do that by defining groups of characters and capturing them using the (...)
"parantheses". In our example above, we write a simple pattern that captures everything from the start of js_
until the extension .txt
.
// Matching Nested Group (..(...))
('18200 BaldwinAve').match(/(\d+ (\w+))/); // capture: "18200 BaldwinAve" and "BaldwinAve" โ
('17200 FairwayDr').match(/(\d+ (\w+))/); // capture: "17200 FairwayDr" and "FairwayDr" โ
We also could do a match a nested group by using (..)
inside another (...)
. In our example above, we are do a match for a street number the outer group is able to capture full address. And the inner group we are able to capture the street name. ๐
// Using quantifier inside our group match (...*)
('1376x768').match(/(\d+)x(\d+)/); // capture: "1366" and "768" โ
('400x200').match(/(\d+)x(\d+)/); // capture: "400" and "200" โ
In the example above, using regex we can get the value of width
and height
of screen resolution. Note that we are using two group match in our pattern \d+
is used to catch all the number.
// Conditional using "pipe" |
/(Apple๐|Orange๐|Pineapple๐) is my favorite fruit/.test('Apple๐ is my favorite fruit'); // match โ
/(Apple๐|Orange๐|Pineapple๐) is my favorite fruit/.test('Pineapple๐ is my favorite fruit'); // match โ
/(Apple๐|Orange๐|Pineapple๐) is my favorite fruit/.test('Strawberry๐ is my favorite fruit'); // skip โ
We also can specify a conditioning in our regex pattern by using |
"pipe". In this example we try to match whether our favorite fruit is either Apple๐
, Orange๐
, or Pineapple๐
. Other than that will be no match.
Congrats for making this far! ๐ You are now know the basic of regex. Let's see our first regex in this article for validating password. Let's glance it once more.
function validatePasswordRegex(input) {
return /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/.test(input);
}
Are you able to know what this regex do now? If you are able to read it, congrats! ๐ Let's walkthrough together.
Our validatePasswordRegex()
has 4 condition for validating a password.
(?=.*\d)
is used to match whether there is a number in our input.(?=.*[a-z])
is used to match if there is a lowercase character in our input.(?=.*[A-Z])
is used to match if there is an uppercase character in our input..{6,}
it used to make sure if there are minimal 6 character in our input.
I hope this article could help you in your journey of learning regex ๐ค. You could try regexr if you want messing around with it. Thank you for reading and good luck! โจ
Posted on April 24, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.