GenAI Course · Module 2 · Session 6
Regular Expressions
Regular expressions (regex) are a tiny language for describing text patterns, such as “two letters, a dash, then five digits”. They look scary at first, but a dozen symbols cover most real work. You will use them to extract data, redact personal information before sending text to an LLM, and clean large datasets.
In this session
- The 12 regex symbols you need most
- Building a pattern step by step
- Extracting emails, dates, invoice numbers, PAN and IFSC codes
- PII redaction before LLM calls
- Cleaning a large corpus quickly
- A live regex tester to practise
01Pattern building blocks
| Symbol | Meaning | Example | Matches |
|---|---|---|---|
\d | any digit | \d\d | 42 |
\w | letter, digit or _ | \w+ | priya_k |
\s | space, tab, newline | a\sb | a b |
. | any character | a.c | abc, a-c |
[A-Z] | one character from a set | [A-Z]{3} | UPI |
+ * ? | one or more / zero or more / optional | colou?r | color, colour |
{n} {n,m} | exactly n / between n and m | \d{4} | 2025 |
^ $ | start / end of the text or line | ^INV | INV at the start |
\b | word boundary | \bUPI\b | UPI, not UPIs |
( ) | group: capture a part | INV-(\d{4}) | captures 2025 |
| | or | cat|dog | cat or dog |
\. | a real dot (escaped) | co\.in | co.in |
Demo · Build a pattern for invoice numbers like INV-2025-00123
Tip: in Python, write patterns as raw strings:
r"\d{4}". Otherwise Python treats the backslash specially first.02Extracting emails, dates, invoice numbers and IDs
regex_extract.py
import re
text = """Invoice INV-2025-00123 dated 05/03/2025 sent to priya.sharma@example.com.
Reminder for INV-2025-00981 on 2025-03-12. Contact: +91 98765 43210 or ravi_k@bank.co.in
PAN ABCDE1234F, IFSC HDFC0001234. Paid 12 March 2025."""
patterns = {
"emails": r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+",
"invoices": r"\bINV-\d{4}-\d{5}\b",
"dates": r"\b\d{2}/\d{2}/\d{4}\b|\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2} (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{4}\b",
"phones": r"\+91[ -]?\d{5}[ -]?\d{5}",
"PAN": r"\b[A-Z]{5}\d{4}[A-Z]\b",
"IFSC": r"\b[A-Z]{4}0[A-Z0-9]{6}\b",
}
for name, p in patterns.items():
print(f"{name:9s}", re.findall(p, text))
# groups: pull out the parts of a match
m = re.search(r"INV-(\d{4})-(\d{5})", text)
print("\nyear:", m.group(1), "| number:", m.group(2))emails ['priya.sharma@example.com', 'ravi_k@bank.co.in']
invoices ['INV-2025-00123', 'INV-2025-00981']
dates ['05/03/2025', '2025-03-12', '12 March 2025']
phones ['+91 98765 43210']
PAN ['ABCDE1234F']
IFSC ['HDFC0001234']
year: 2025 | number: 00123
re.findallreturns every match;re.searchreturns the first match with its groups.- The date pattern uses
|to accept three formats. - PAN format: 5 letters, 4 digits, 1 letter. IFSC: 4 letters, a zero, then 6 letters or digits.
Try it yourself. Pick a pattern or type your own; matches light up instantly:
Demo · Live regex tester
03PII redaction and corpus cleaning
Before sending customer text to an outside LLM, or storing it for training, replace personal data with labels. re.sub does find-and-replace with patterns:
regex_redact.py
import re
RULES = [
(r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+", "[EMAIL]"),
(r"\+91[ -]?\d{5}[ -]?\d{5}|\b[6-9]\d{9}\b", "[PHONE]"),
(r"\b[A-Z]{5}\d{4}[A-Z]\b", "[PAN]"),
(r"\b\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?\d{4}\b", "[CARD]"),
]
def redact(text: str) -> str:
for pattern, label in RULES:
text = re.sub(pattern, label, text)
return text
def clean(text: str) -> str:
text = re.sub(r"<[^>]+>", " ", text) # remove HTML tags
text = re.sub(r"https?://\S+", "[URL]", text) # replace links
text = re.sub(r"\s+", " ", text) # collapse spaces and newlines
return text.strip()
raw = """<p>Hi, I am Ravi. My card 4111 1111 1111 1111 was charged twice!</p>
<p>Call me on 9876543210 or mail ravi.k@gmail.com. PAN: ABCDE1234F</p> See https://bank.example/help"""
print(redact(clean(raw)))
# speed on a large corpus: compile once, reuse
pattern = re.compile(RULES[0][0])
corpus = ["write to support@bank.in today"] * 100_000
import time; t = time.perf_counter()
hits = sum(1 for line in corpus if pattern.search(line))
print(f"\n{hits:,} lines matched in {time.perf_counter() - t:.2f} s")Hi, I am Ravi. My card [CARD] was charged twice! Call me on [PHONE] or mail [EMAIL]. PAN: [PAN] See [URL]
100,000 lines matched in 0.07 s
- Compile once with
re.compilewhen you run the same pattern on millions of lines. - Order matters: redact cards before phones, or a 16-digit card could be partly matched as a phone number.
- Regex finds formats, not meaning: it cannot spot a name like “Ravi”. Combine it with NER (Session 5) or a PII tool such as Microsoft Presidio.
Avoid catastrophic patterns. Nested repeats like
(a+)+ can take forever on some inputs. Keep patterns specific and test them on bad data.04Summary
- A dozen symbols (
\d \w \s . [ ] + * ? {n} ^ $ \b ( ) |) cover most regex work. - Build patterns step by step and add
\bboundaries to avoid accidental matches. findallextracts,search+ groups pulls out parts,subreplaces.- Redact PII (emails, phones, PAN, cards) before text reaches an LLM.
- Regex matches formats; use NER for names and meaning.
05Quick quiz
1. What does \d{4} match?
2. Why add \b around a pattern?
3. Which function replaces matches?
4. Why redact PII before calling an LLM API?
5. Can regex reliably find people’s names?
06References
- Python re documentation
- Python Regular Expression HOWTO
- regex101 (online tester)
- Microsoft Presidio (PII detection)
Created by Ajeetkumar