Regular expressions you can see
·
Course overview
1
The missing payment: finding text as it is
2
The hit-and-run car: character classes
3
The torn phone book: quantifiers
4
The fake sign-ups: anchors and validation
5
Dates all over the place: groups and alternation
6
The 3 a.m. login: named groups and Match objects
7
The night before the story: substitution and splitting
8
The Backtracker's challenge: greedy, lazy and possessive
9
The safe's rules: lookaround
10
Same modus operandi: backreferences and conditionals
11
The unreadable regex: flags and compiling
12
Unmasking the Backtracker: review and a tokenizer
CASE-001
Grep & Co. Data Detective Agency
Case file
Client
Owner of the "Level Up" PC café
Evidence
The payment server log. A customer insists he paid $4.99, but there's no record of it.
To close the case
Find the lines in the log where an error occurred, and pick out only the payments of exactly $4.99.
Your first morning. The director sets down her coffee cup and slides a sheet of paper across the desk. "The PC café owner came by yesterday. Customer says he paid $4.99, but there's no record. Logs don't lie. Go find it."
Here is the log. Today you hunt for evidence in these five lines.
2026-10-20 09:12:01 INFO PAY user=kim amount=$4.99 status=OK
2026-10-20 09:12:07 INFO PAY user=lee amount=$14.99 status=OK
2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99 status=TIMEOUT
2026-10-20 09:15:02 INFO PAY user=choi amount=$4999 status=OK
2026-10-20 09:16:30 ERROR DB connection lostA regular expression (regex) is a tiny language for describing the shape of the text you want to find. In Python you use it through the re module. The simplest regex is just plain text: the pattern ERROR finds the text ERROR.
import re
log = "2026-10-20 09:13:44 ERROR PAY user=park"
m = re.search("ERROR", log)
print(m) # <re.Match object; span=(20, 25), match='ERROR'>re.search(pattern, string) scans the string from the start and finds the first place where the pattern appears.None.Evidence lab
re.search(r"", text)
Return value
A Match object is proof that something was found, and also a report of what was found and where.
m = re.search("ERROR", log)
if m: # truthy if found, None is falsy
print(m.group()) # ERROR (the matched text)
print(m.span()) # (20, 25) (start position, end position)if m:. When nothing is found you get None, and calling m.group() on it raises an AttributeError.span() is not included. It's the same rule that makes log[20:25] equal "ERROR".Most characters simply find themselves. But the 14 below are metacharacters with a special meaning in a regex.
. ^ $ * + ? { } [ ] \ | ( )Today you learn only the first one, .. The dot means any single character except a newline. The rest come out one lesson at a time.
re.search("a.c", "abc") # match: b sits in the . spot
re.search("a.c", "a-c") # match: - counts as any character too
re.search("a.c", "ac") # None: . always takes up exactly one character
re.search("a.c", "a\nc") # None: a newline is not a .Dot's tip
I climb onto anything in this office: desks, chairs, even the director's keyboard. There's just one thing I can't get past, and that's the window (a newline). That's why my name is Dot (.). They say that someday, if somebody opens the window for me, I'll be able to hop through.
Evidence lab
re.search(r"", text)
Return value
Back to the case. Say the five log lines above are stored in a string called pay_log. What happens if you look for the $4.99 payments by writing 4.99?
re.findall("4.99", pay_log) # ['4.99', '4.99', '4.99', '4999'] ← four?There should be two results, and there are four. The tail end of $14.99 got caught, and because . means "any character", the 4999 in $4999 slipped in too. To find a metacharacter as the literal character, put a backslash \ in front of it. \. is one real period.
$ is a metacharacter as well. You'll learn what it means in Lesson 4; for now, just remember that written bare, it does not look for a dollar sign. So the answer is \$4\.99.
re.findall(r"\$4\.99", pay_log) # ['$4.99', '$4.99']Evidence lab
re.search(r"", text)
Return value
Above, we put an r in front of the quotes, as in r"\$4\.99". This is called a raw string.
Backslashes are special in Python strings too: "\n" is a newline, "\t" is a tab. So if you write a regex as a normal string, Python interprets the backslashes first, and re receives a completely different pattern.
print(len("\b")) # 1 ← Python turned \b into a single 'backspace' character
print(len(r"\b")) # 2 ← a backslash and a b, two characters as writtenr"..." hands the backslashes to re untouched. Always write regexes as raw strings."\d", in a normal string gives a SyntaxWarning. With a raw string there's no warning.The PC café owner adds, "The customer's ID is kim.99+. Can you find that too?" Write text like this directly as a pattern and the . and + are read as metacharacters. When you need to find text a person typed exactly as typed, re.escape puts a backslash in front of every metacharacter for you.
print(re.escape("kim.99+")) # kim\.99\+
re.search(re.escape("kim.99+"), "user=kim.99+") # matchTo sum up: the lines with an error are found with ERROR, and payments of exactly $4.99 are found with \$4\.99. Put the two conditions together and it comes out that customer park's payment was never recorded because of a TIMEOUT error.
for line in pay_log.splitlines():
if re.search("ERROR", line) and re.search(r"\$4\.99", line):
print(line)
# 2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99 status=TIMEOUTCase closed report — At 09:13:44 the payment server failed the payment due to a timeout, while the customer's screen showed it as complete. Advised the PC café owner to issue a refund.
INFO. How many are there?\$1.\.99 find? Make a guess before you check it in the lab.re.escape("1+1=2? [yes]"), then run it yourself.The director's notebook
Most characters find themselves. Only the 14 metacharacters . ^ $ * + ? { } [ ] \ | ( ) are special.
. is any single character except a newline. A real period is \.
re.search returns a Match or None. Test it with if m: and inspect it with m.group(), m.span().
Always write regexes as r"..." raw strings.
To find text a person typed exactly as typed, use re.escape.
Get news about new lessons
We'll email you when this course gets new lessons or a follow-up course opens.
R
ReflexKR
Learn Python and Reflex by doing. Every example was run and verified on Reflex 0.10.0.
ReflexKR is free, with no banner ads. If it helped you, you can support it with a coffee. ☕ Buy us a coffee
Affiliate disclosure© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Questions & answers
Ask anything about this lesson. Questions from every language version of this page appear together.