R

ReflexKR

Reflex Docs

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

Regular expressions you can see
Lesson 1

Time 0:00–0:50

The missing payment: finding text as it is

CASE-001

Grep & Co. Data Detective Agency

Case file

The missing payment

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 lost

1-1. A regex is a "text pattern" (5 min)

A 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.
  • If it finds one, it returns a Match object; if not, it returns None.

Evidence lab

re.search(r"", text)

Return value

Switch between search and findall. search finds only the first one; findall finds them all.

1-2. Did it match, and where? The Match object (5 min)

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)
  • Test it directly for truth, as in if m:. When nothing is found you get None, and calling m.group() on it raises an AttributeError.
  • The end position of span() is not included. It's the same rule that makes log[20:25] equal "ERROR".
  • These exact values show up in the "First match" panel of the Evidence lab.

1-3. The 14 special characters (10 min)

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

a.c matches only when there is exactly one character between a and c.

1-4. Switching off the special meaning with a backslash (10 min)

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

Start from 4.99, then add \. and \$ one at a time. You're done when every case condition turns green.

1-5. Why write r"...": raw strings (5 min)

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 written
  • r"..." hands the backslashes to re untouched. Always write regexes as raw strings.
  • Since Python 3.12, writing an escape Python doesn't know, such as "\d", in a normal string gives a SyntaxWarning. With a raw string there's no warning.

1-6. Finding exactly what a customer typed: re.escape (5 min)

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+")    # match

Cracking the case (10 min)

To 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=TIMEOUT

Case 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.

Exercises

  1. Find every line in the log that says INFO. How many are there?
  2. What does \$1.\.99 find? Make a guess before you check it in the lab.
  3. Predict the result of 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.

← Previous

Course overview

Next →

Lesson 2 · The hit-and-run car: character classes

Get news about new lessons

We'll email you when this course gets new lessons or a follow-up course opens.

We collect: your email address · Purpose: news about new lessons or the launch of this course · Kept until: you ask us to remove it. We never use it for anything else or share it. Reply to any notice email and we'll delete it right away.

Questions & answers

Ask anything about this lesson. Questions from every language version of this page appear together.

Shown as plain text, without links or formatting.

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
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex