← 피드로
Regular Expressions: The Guide I Always Wanted (2026)
Regex looks like gibberish until it clicks — then it’s a superpower. Here’s the guide that makes regex actually make sense.
The Mental Model
Think of regex as a pattern-matching machine:
Input string: "[email protected]"
Pattern: w+@w+.w+
The engine scans left-to-right, trying to match your pattern
at each position. When the full pattern matches → SUCCESS!
Key insight: Regex isn't about "finding text" — it's about
defining the SHAPE of what you're looking for.
Enter fullscreen mode Exit fullscreen mode
Character Classes: What Are We Matching?
// Literal characters (match exactly)
/hello/ // Matches "hello" in "say hello there"
// Dot (wildcard) — matches ANY single character except newline
/h.t/ // "hat", "hot", "hit", but NOT "h" or "ht"
// Character classes — match ONE character from a set:
/[aeiou]/ // Any vowel
/[a-z]/ // Any lowercase letter
/[A-Z]/ // Any uppercase letter
/[0-9]/ // Any digit (same as /d/)
/[a-zA-Z0-9]/ // Any alphanumeric (same as /w/)
/[^0-9]/ // NOT a digit (negation with ^ inside [])
/[^aeiou]/ // Not a vowel
// Shorthand classes (use these!):
/d/ // Digit: [0-9]
/D/ // Non-digit: [^0-9]
/w/ // Word char: [a-zA-Z0-9_]
/W/ // Non-word char: [^w]
/s/ // Whitespace: [ trnfv]
/S/ // Non-whitespace: [^s]
// Examples:
/d{3}-d{4}/ // Phone-like: "555-1234"
/[A-Z][a-z]+/ // Capitalized word: "Hello", "World"
/#[0-9a-fA-F]{6}/ // Hex color code: "#ff5500"
Enter fullscreen mode Exit fullscreen mode
Quantifiers: How Many Times?
// Quantifiers apply to the PRECEDING element:
/a*/ // Zero or more a's ("" is valid match!)
/a+/ // One or more a's (at least one)
/a?/ // Zero or one a (optional)
/a{3}/ // Exactly 3 a's ("aaa")
/a{2,4}/ // 2 to 4 a's ("aa", "aaa", "aaaa")
/a{2,}/ // 2 or more a's (no upper limit)
// ⚠️ Greedy vs Lazy (CRITICAL concept!)
// GREEDY (default): Match as MUCH as possible
const html = '<div>first</div><div>second</div>';
html.match(/<div>.*</div>/);
// Matches: '<div>first</div><div>second</div>' (greedy eats everything!)
// LAZY: Match as LITTLE as possible (add ? after quantifier)
html.match(/<div>.*?</div>/);
// Matches: '<div>first</div>' (lazy stops at first opportunity)
// Real-world example: Extract content between quotes
const str = 'He said "hello" and she said "world"';
str.match(/"(.*?)"/g); // [""hello"", ""world""]
// Without lazy: would match from first " to last "
Enter fullscreen mode Exit fullscreen mode
Anchors & Boundaries
// Anchors don't match characters — they match POSITIONS:
/^Hello/ // "Hello" at START of string only
/world$/ // "world" at END of string only
/^Hello world$/ // Exact full-string match
// Word boundaries (b) — position between word char and non-word char:
/bcatb/ // Matches "cat" but NOT "catalog" or "scattered"
// Useful for whole-word search!
// Common patterns using anchors:
/^[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,}$/i // Email validation
/^https?://[^s]+$/i // URL detection
/^d{4}-d{2}-d{2}$/ // Date YYYY-MM-DD
/^[+-]?d+(.d+)?$/ // Integer or decimal number
Enter fullscreen mode Exit fullscreen mode
Groups & Capturing
// Capturing groups (...) — extract parts of the match:
const date = '2024-06-11';
date.match(/(d{4})-(d{2})-(d{2})/);
// Full match: "2024-06-11"
// Group 1 (year): "2024"
// Group 2 (month): "06"
// Group 3 (day): "11"
// Named capture groups (much more readable!):
const result = '[email protected]'.match(/(?<name>w+)@(?<domain>w+).(?<tld>w+)/);
result.groups.name; // "user"
result.groups.domain; // "domain"
result.groups.tld; // "com"
// Non-capturing group (?:...) — group without capturing:
/(?:https?://)?(www.w+.w+)/
// First group doesn't capture, second does
// Backreferences — refer to earlier captured group:
/(w+) 1/ // Matches "word word" (repeated word!)
/<(w+)>.*?</1>/ // Matches <b>...</b> but NOT <b></i>
// Lookahead assertions (match based on what FOLLOWS):
/d+(?= dollars)/ // Match numbers only if followed by " dollars"
// "I have 100 dollars" → matches "100"
// "I have 100 euros" → no match
// Negative lookahead:
/d+(?! dollars)/ // Match numbers NOT followed by " dollars"
// "I have 100 euros" → matches "100"
// "I have 100 dollars" → no match
// Lookbehind (what PRECEDES):
/(?<=$)d+/ // Match digits preceded by $
// "Price: $99" → matches "99"
/(?<!$)d+/ // Match digits NOT preceded by $
// "Price: $99, qty 5" → matches "5" only
Enter fullscreen mode Exit fullscreen mode
Practical Examples You’ll Actually Use
// === Validation ===
function validateEmail(email) {
return /^[^s@]+@[^s@]+.[^s@]+$/.test(email);
// Simple but practical. Don't try RFC-compliant email regex!
}
function validatePassword(password) {
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[!@#$%^&*]).{12,}$/.test(password);
}
function validateURL(url) {
try { new URL(url); return true; }
catch { return false; } // URL constructor > regex for URLs!
}
// === Extraction & Transformation ===
// Extract all hashtags:
'Check out #JavaScript and #WebDev'.match(/#w+/g);
// ["#JavaScript", "#WebDev"]
// Format phone number:
'5551234567'.replace(/(d{3})(d{3})(d{4})/, '($1) $2-$3');
// "(555) 123-4567"
// CamelCase to snake_case:
'myVariableName'.replace(/[A-Z]/g, '_$&').toLowerCase();
// "my_variable_name"
// Truncate words to max length:
'this is a very long sentence that needs shortening'.replace(/b(w{1,8})s?b/g, '$1n');
// Break into lines where words exceed 8 chars
// Remove duplicate lines:
text.split('n').filter((line, i, arr) => arr.indexOf(line) === i).join('n');
// Find unquoted strings (complex!):
// Match strings not surrounded by quotes:
/'[^']*'|"[^"]*"|(bw{3,}b)/g // Then filter out captured group 1
// === Search & Replace in Code ===
// Add console.log before each function line (for debugging):
code.replace(/^(functions+w+)/gm, 'console.log("$1"); $1');
// Convert require() to import:
code.replace(/consts+(w+)s*=s*require(['"](.*)['"])/g, 'import $1 from "$2";');
// Remove console.log statements (before deploy):
code.replace(/^s*console.(log|debug|info)(.*);s*$/gm, '');
Enter fullscreen mode Exit fullscreen mode
Testing & Debugging Regex
// Test in browser console or Node.js:
const pattern = /your-regex-here/;
pattern.test('test string'); // true/false
'test string'.match(pattern); // Array of matches or null
'test string'.replaceAll(pattern, 'replacement');
// Debugging tips:
// 1. Start simple, build up piece by piece
// 2. Use regex101.com or regexr.com for visual debugging
// 3. Use .source to see the actual pattern string:
console.log(pattern.source);
// 4. Break complex patterns into pieces:
const WORD = /[a-zA-Z]+/;
const SPACE = /s+/;
const sentencePattern = new RegExp(`^${WORD.source}(${SPACE.source}${WORD.source})*$`);
// 5. Common gotchas:
// - In JS strings, backslashes must be escaped: new RegExp('\d+') not new RegExp('d+')
// - . doesn't match newline by default; use [sS] instead if needed
// - Always use ^ and $ anchors for full-string validation
// - test() returns true on partial match; use anchors for exact matching
Enter fullscreen mode Exit fullscreen mode
What’s your favorite regex trick? What regex problem has been haunting you?
Follow @armorbreak for more practical developer guides.
추출 본문 · 출처: dev.to · https://dev.to/armorbreak/regular-expressions-the-guide-i-always-wanted-2026-3j1j
답글 남기기