Regular Expressions
Pattern matching with Python's re module — character classes, quantifiers, anchors, capturing groups, search/match/findall/split/sub, and practical validation (emails, phone numbers, passwords).
Sometimes checking text with plain string methods (.find(), .replace()) isn't enough — what if you need to check "is this a valid email?" or "does this text contain any 10-digit phone number?" Regular expressions (regex) are a mini-language for describing text patterns, and Python's built-in re module lets you use them.
1. What are Regular Expressions?
What is it?
A regular expression is a sequence of characters that defines a search pattern — used to match, find, or validate specific patterns within text.
Definition: A regular expression (regex) is a pattern used to match, search, or manipulate text based on specific rules.
Why do we use it?
- Validating input formats: emails, phone numbers, passwords, postal codes.
- Extracting specific pieces of information from larger text (like pulling all phone numbers out of a document).
- Searching and replacing text based on flexible patterns, not just exact matches.
How does it work?
Python's re module reads a pattern (written using regex syntax) and applies it to a string, looking for matches.
pythonimport re
Every example in this file assumes re has been imported.
2. Basic Regex Syntax
Character Classes
| Pattern | Matches |
|---|---|
\d | Any digit (0-9) |
\D | Any non-digit |
\w | Any word character (letters, digits, underscore) |
\W | Any non-word character |
\s | Any whitespace (space, tab, newline) |
\S | Any non-whitespace |
. | Any character except a newline |
Simple Example
pythonimport re text = "My phone number is 9876543210" match = re.search(r"\d+", text) print(match.group()) # 9876543210
Explanation: \d+ means "one or more digits in a row." re.search() scans the text and finds the first match.
Quantifiers — Controlling How Many Times
| Quantifier | Meaning |
|---|---|
* | 0 or more |
+ | 1 or more |
? | 0 or 1 (optional) |
{n} | Exactly n times |
{n,m} | Between n and m times |
pythonprint(re.search(r"\d{10}", "Call 9876543210 now").group()) # 9876543210 (exactly 10 digits) print(re.search(r"colou?r", "color").group()) # color (u is optional) print(re.search(r"colou?r", "colour").group()) # colour
Anchors — Matching Position
| Anchor | Meaning |
|---|---|
^ | Start of the string |
$ | End of the string |
pythonprint(bool(re.match(r"^Hello", "Hello World"))) # True - starts with "Hello" print(bool(re.search(r"World$", "Hello World"))) # True - ends with "World"
Common Mistakes
- Forgetting that
.matches almost any character — if you want a literal dot (like in an email or file extension), you must escape it:\. - Forgetting quantifiers apply only to the character/group immediately before them.
Important Points
- Always use raw strings (
r"...") for regex patterns, to avoid Python misinterpreting backslashes. \d,\w,\s(and their uppercase opposites) are the most frequently used building blocks.
3. Groups and Capturing Groups
What is it?
Parentheses () in a regex pattern create a group — a way to isolate and extract just a specific part of a match, rather than the whole thing.
Simple Example
pythontext = "Contact: aditi@example.com" match = re.search(r"(\w+)@(\w+)\.(\w+)", text) print(match.group()) # aditi@example.com (the full match) print(match.group(1)) # aditi (first group) print(match.group(2)) # example (second group) print(match.group(3)) # com (third group)
Explanation of the Code
- Each pair of parentheses is a separate "capturing group," numbered left to right starting from 1.
match.group()(with no number, or0) gives the entire matched text;match.group(1),match.group(2), etc. give just the parts inside each specific group.
Real-World Example
Extracting the username and domain separately from an email address, or extracting the area code, exchange, and line number separately from a phone number.
Important Points
- Groups let you extract specific parts of a larger match, not just confirm that a pattern exists.
- Group numbering starts at 1;
group(0)(or just.group()) always refers to the whole match.
4. Core re Functions
re.search() — Find the First Match Anywhere in the String
pythonresult = re.search(r"\d+", "Order number: 4521") print(result.group()) # 4521
re.match() — Match Only at the Very Start of the String
pythonprint(re.match(r"\d+", "4521 is the order number")) # matches (starts with digits) print(re.match(r"\d+", "Order number: 4521")) # None (doesn't start with digits)
re.findall() — Find ALL Matches, Returned as a List
pythontext = "Call 9876543210 or 8765432109" numbers = re.findall(r"\d{10}", text) print(numbers) # ['9876543210', '8765432109']
re.split() — Split Text Using a Pattern
pythontext = "apple, banana; cherry,mango" items = re.split(r"[,;]\s*", text) print(items) # ['apple', 'banana', 'cherry', 'mango']
Explanation: This splits on a comma or semicolon, optionally followed by whitespace — handling messy, inconsistent formatting in one pattern.
re.sub() — Search and Replace
pythontext = "My number is 9876543210" masked = re.sub(r"\d{10}", "XXXXXXXXXX", text) print(masked) # My number is XXXXXXXXXX
Comparison Table — Core re Functions
| Function | Purpose | Returns |
|---|---|---|
re.search() | Find the first match anywhere | Match object (or None) |
re.match() | Match only at the start of the string | Match object (or None) |
re.findall() | Find every match | List of strings |
re.split() | Split text using a pattern | List of strings |
re.sub() | Replace matches with new text | New string |
Common Mistakes
- Confusing
re.match()(start of string only) withre.search()(anywhere in the string) — a very common source of "why doesn't my pattern match?" confusion. - Forgetting that
search()/match()returnNonewhen there's no match — calling.group()onNonecauses anAttributeError. Always check the result first withif match:.
Practice
- Use
re.findall()to extract all numbers from the text"I have 3 cats, 2 dogs, and 1 fish". - Use
re.sub()to replace all vowels in a sentence with*.
5. Practical Validation Examples
5.1 Email Validation
pythondef is_valid_email(email): pattern = r"^[\w.-]+@[\w.-]+\.\w+$" return bool(re.match(pattern, email)) print(is_valid_email("aditi@example.com")) # True print(is_valid_email("invalid-email")) # False
Explanation: ^[\w.-]+ matches the part before @ (letters, digits, dots, hyphens). @[\w.-]+ matches the domain name. \.\w+$ requires a dot followed by letters (the extension, like .com), anchored to the end of the string.
5.2 Phone Number Validation
pythondef is_valid_phone(number): pattern = r"^\d{10}$" return bool(re.match(pattern, number)) print(is_valid_phone("9876543210")) # True print(is_valid_phone("98765")) # False
5.3 Password Validation
pythondef is_strong_password(password): has_length = len(password) >= 8 has_upper = bool(re.search(r"[A-Z]", password)) has_digit = bool(re.search(r"\d", password)) has_special = bool(re.search(r"[!@#$%^&*]", password)) return has_length and has_upper and has_digit and has_special print(is_strong_password("Python@123")) # True print(is_strong_password("weakpass")) # False
5.4 Extracting Numbers From Text
pythontext = "Room 42B, Floor 3, Building 7" numbers = re.findall(r"\d+", text) print(numbers) # ['42', '3', '7']
5.5 Finding Words
pythontext = "Python is fun and Python is powerful" words = re.findall(r"\b\w+\b", text) print(words) # ['Python', 'is', 'fun', 'and', 'Python', 'is', 'powerful'] python_count = len(re.findall(r"\bPython\b", text)) print(python_count) # 2
Explanation: \b marks a "word boundary" — the edge between a word character and a non-word character — ensuring \bPython\b matches the whole word "Python" and not, say, the "Python" inside "Pythonic."
Common Mistakes
- Writing overly simple email patterns that miss valid formats (real-world email validation regex can get quite complex — most production systems combine regex with sending an actual verification email).
- Forgetting
\bwhen searching for whole words, causing partial matches inside longer words.
Common Beginner Mistakes — Summary for This Section
- Forgetting to use raw strings (
r"...") for patterns. - Confusing
re.match()(start only) withre.search()(anywhere). - Forgetting to escape literal special characters like
.when they should be matched literally. - Calling
.group()on aNoneresult without checking first.
Cheat Sheet — Regular Expressions
pythonimport re \d \D \w \W \s \S . # character classes * + ? {n} {n,m} # quantifiers ^ $ # anchors (start / end) () # capturing group \b # word boundary re.search(pattern, text) # first match anywhere re.match(pattern, text) # match at start only re.findall(pattern, text) # all matches as a list re.split(pattern, text) # split by pattern re.sub(pattern, replacement, text) # find and replace match.group() # full match match.group(1) # first captured group
Interview Questions
Q1. What is the difference between `re.match()` and `re.search()`? Answer: re.match() only checks for a match at the very beginning of the string. re.search() scans the entire string and returns the first match found anywhere.
Q2. What does `re.findall()` return? Answer: A list of all non-overlapping matches found in the string.
Q3. What is a capturing group in regex? Answer: A portion of the pattern enclosed in parentheses (), allowing you to extract just that specific part of the overall match.
Q4. Why should regex patterns be written as raw strings in Python? Answer: To prevent Python from interpreting backslashes as escape sequences before the regex engine gets to process them — raw strings (r"...") treat backslashes literally.
Q5. What does `\b` mean in a regex pattern? Answer: A word boundary — the position between a word character and a non-word character, useful for matching whole words rather than partial matches inside longer words.
Practice Questions
Beginner
- Write a regex pattern to check if a string contains only digits.
- Extract all email addresses from a paragraph of text using
re.findall(). - Check whether a string starts with "Mr." or "Mrs." using
re.match(). - Replace all digits in a string with the
#symbol usingre.sub(). - Split a sentence into individual words using regex.
Intermediate
- Write a function to validate Indian-style phone numbers (10 digits, optionally starting with +91).
- Write a function to validate a password requiring at least 8 characters, one digit, one uppercase letter, and one special character.
- Extract all hashtags (words starting with
#) from a social media post using regex. - Write a program that extracts all dates in the format
DD-MM-YYYYfrom a block of text. - Use capturing groups to separate a full name into first and last name from a string like
"Aditi Sharma".
Challenge
- Write a function that validates a URL, checking it starts with
http://orhttps://and has a valid domain structure. - Write a program that extracts all valid email addresses from a messy block of text that also contains invalid-looking addresses, and prints only the valid ones.
- Build a simple log-line parser using regex that extracts the timestamp, log level (INFO/ERROR/WARNING), and message from a line like
[2026-01-15 10:30:00] ERROR: Database connection failed.