Day 23: Regular Expressions for Real Validation
✅ Today you will learn
- What a regular expression is and how to read one
- Character classes, quantifiers, anchors
- Groups, alternation, named groups and flags
- test, match, matchAll, replace with patterns
- Real Indian validations – and when NOT to use regex
🏢 In the real world
A B2B wholesale form for Sharma Sweets accepts orders from shops. It needs the shop’s GSTIN, a PIN code and a mobile number in many formats (+91 98290-12345, 09829012345, 98290 12345). Writing that with if/else and indexOf is hundreds of lines; regular expressions do it in one line each – once you can read them.
Reading a regular expression
const pin = /^[1-9][0-9]{5}$/; // Indian PIN code
console.log(pin.test('302003')); // true
console.log(pin.test('012345')); // false – cannot start with 0
console.log(pin.test('30200')); // false – only 5 digits
console.log(pin.test('302003a')); // false – $ means the string must end here| Token | Matches | Example |
|---|---|---|
| \d / \D | A digit / a non-digit | \d{10} = ten digits |
| \w / \s | Letter, digit or _ / whitespace | \s+ = one or more spaces |
| [abc] / [^abc] | One of a, b, c / anything except | [A-Z] one capital letter |
| . | Any character except a new line | \. for a real dot |
| * + ? | 0 or more / 1 or more / 0 or 1 | colou?r |
| {n} {n,} {n,m} | Exactly n / at least n / n to m | \d{4,6} |
| ^ $ | Start / end of text | Whole-value validation |
| \b | Word boundary | \bkaju\b |
| ( ) | | Group / OR | (milk|dry) |
| (? | Named group | (? |
Real Indian validations
const patterns = {
mobile: /^(?:\+?91[\s-]?|0)?[6-9]\d{4}[\s-]?\d{5}$/, // +91 98290-12345, 09829012345 …
pin: /^[1-9]\d{5}$/,
gstin: /^\d{2}[A-Z]{5}\d{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/, // 15 chars: state code + PAN + entity + Z + checksum
pan: /^[A-Z]{5}\d{4}[A-Z]$/,
ifsc: /^[A-Z]{4}0[A-Z0-9]{6}$/,
vehicle:/^[A-Z]{2}[ -]?\d{1,2}[ -]?[A-Z]{1,3}[ -]?\d{4}$/, // RJ 14 CX 1234
};
const tests = {
mobile: ['+91 98290-12345', '09829012345', '9829012345', '5829012345', '98290123'],
pin: ['302003', '102003', '002003'],
gstin: ['08ABCDE1234F1Z5', '08ABCDE1234F1X5'],
pan: ['ABCDE1234F', 'ABCD1234F'],
ifsc: ['SBIN0001234', 'SBIN1001234'],
vehicle: ['RJ 14 CX 1234', 'RJ14CX1234', 'R 14 CX 1234'],
};
for (const [name, values] of Object.entries(tests)) {
console.log(name.padEnd(8), values.map(v => `${v} ${patterns[name].test(v) ? '✅' : '❌'}`).join(' '));
}A regex only checks the SHAPE. 08ABCDE1234F1Z5 has a valid GSTIN format but may not exist; a mobile number may be switched off. Real verification needs a checksum, an OTP or a government API on the server. Also never store PAN or Aadhaar numbers unless you truly must and have consent.
Normalise first, then validate
function normaliseMobile(input) {
const digits = input.replace(/\D/g, ''); // remove everything that is not a digit
const ten = digits.replace(/^(91|0)(?=\d{10}$)/, ''); // drop a leading 91 or 0 if 10 digits remain
return /^[6-9]\d{9}$/.test(ten) ? ten : null;
}
['+91 98290-12345', '(0) 98290 12345', '919829012345', '12345'].forEach(v => console.log(v.padEnd(18), '→', normaliseMobile(v)));
const pretty = n => n.replace(/^(\d{5})(\d{5})$/, '+91 $1 $2'); // $1, $2 = captured groups
console.log(pretty('9829012345'));Searching, extracting and replacing
const sms = 'Order #1042 for ₹1,800 confirmed. Order #1043 for ₹650 pending. Call 98290 12345.';
console.log(/order #\d+/i.test(sms)); // i = case-insensitive
console.log(sms.match(/#\d+/g)); // g = all matches
for (const m of sms.matchAll(/#(?<id>\d+) for ₹(?<amount>[\d,]+) (?<status>\w+)/g)) {
const { id, amount, status } = m.groups; // named groups
console.log(id, Number(amount.replace(/,/g, '')), status);
}
console.log(sms.replace(/\d{5} \d{5}/, '***** *****')); // mask a phone number
console.log('kaju katli box'.replace(/\s+/g, ' ')); // collapse spaces
console.log('Ghewar,Rasmalai; Ladoo | Peda'.split(/\s*[,;|]\s*/));| Method | Returns | Use |
|---|---|---|
| regex.test(str) | true / false | Validation |
| str.match(regex) | First match (or all with g), or null | Extract values |
| str.matchAll(regex /g) | Iterator of matches with groups | Extract many structured values |
| str.replace(regex, x) | New string | Clean, mask, format; $1 refers to groups |
| str.split(regex) | Array | Split on several separators |
| new RegExp(text, "gi") | A regex built from a string | Dynamic search (escape user input!) |
Real task: highlight search terms safely
<input id="q" value="ka" placeholder="Search" aria-label="Search">
<ul id="list"></ul>
<script>
const items = ['Kaju Katli', 'Kalakand', 'Malai Ghewar', 'Besan Ladoo', 'Soan Papdi (1+1 offer)'];
const escape = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // treat user text literally
function render() {
const q = document.querySelector('#q').value.trim();
const list = document.querySelector('#list');
list.innerHTML = '';
const re = q ? new RegExp(`(${escape(q)})`, 'gi') : null;
for (const name of items) {
if (re && !name.match(re)) continue;
const li = document.createElement('li');
if (!re) { li.textContent = name; list.append(li); continue; }
name.split(re).forEach((part, i) => { // split keeps captured groups
const node = i % 2 ? document.createElement('mark') : document.createTextNode(part);
if (i % 2) node.textContent = part;
li.append(node);
});
list.append(li);
}
}
document.querySelector('#q').addEventListener('input', render);
render();
</script>Do not parse HTML, JSON or full e-mail RFC rules with regex. Use DOMParser, JSON.parse and a simple e-mail check (/^[^\s@]+@[^\s@]+\.[^\s@]+$/) plus a confirmation e-mail. Keep regexes short, commented and tested.
🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
const strong = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$/;
for (const p of ['sweets123', 'Sweets123', 'Sweets@123', 'S@1a']) {
console.log(p.padEnd(11), strong.test(p) ? 'strong ✅' : 'weak ❌');
}Show solution
const bill = 'Kaju Katli ₹1,800; Ghewar ₹650.50; Delivery ₹60; Discount -₹100';
const amounts = [...bill.matchAll(/(-?)₹([\d,]+(?:\.\d{1,2})?)/g)]
.map(m => (m[1] ? -1 : 1) * Number(m[2].replace(/,/g, '')));
console.log(amounts, 'Total:', amounts.reduce((a, b) => a + b, 0));Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
How would you validate an Indian mobile number?
Normalise by stripping non-digits and an optional 91/0 prefix, then check /^[6-9]\d{9}$/. Show a friendly message for invalid input, and verify ownership with an OTP on the server – format checks alone are not proof.
What are greedy and lazy quantifiers?
Greedy quantifiers (*, +, {n,}) match as much as possible; adding ? (*?, +?) makes them lazy, matching as little as possible – e.g. /<.+?>/ matches one tag instead of everything between the first < and the last >.
Summary
- Regex = pattern of classes, quantifiers, anchors and groups.
- Use ^ … $ for full-value validation; normalise input first.
- test to validate, matchAll/groups to extract, replace to clean and mask.
- Escape user input; regex checks format, not truth.
Day 24: higher-order functions, debounce and throttle – make search boxes and scroll handlers fast.
