Regular expressions

Part of the free LogicWiz Python course, Pack B: College Semester Pack. The examples and practice run in the app.

The fest registration sheet has 800 rows typed by students: "98765 43210", "+91-9876543210", "riya@gmail,com". Checking each by hand is impossible. A regular expression checks a pattern in one line.

You'll learn to

  • Read and write patterns with `\d`, `\w`, `[...]`, `{n}`, `+`, `*`, `?`, `^` and ` Regular expressions: Python Course | LogicWiz
  • Use `re.search`, `re.fullmatch`, `re.findall` and `re.sub`, and groups
  • Validate Indian mobile numbers, emails and PIN codes

Learn

A pattern language

A regular expression (regex) describes a shape of text. Always write it as a raw string r"...".

Token Matches
\d a digit 0-9
\w a letter, digit or _
\s a space, tab or newline
. any character
[6-9] one of 6, 7, 8, 9
[A-Z] a capital letter
x+ x* x? one or more, zero or more, optional
x{6} x{2,4} exactly 6, 2 to 4
^ $ start, end of the text
(...) a group you can pull out
\. a real dot

re.search finds the first match

import re

msg = "Hi, this is Riya. Call me on 9876543210 after 5 pm."
m = re.search(r"[6-9]\d{9}", msg)
if m:
    print("found:", m.group())
    print("at position", m.start())

print(re.search(r"\d{12}", msg))

re.search scans the text and returns a Match object, or None if nothing matches. Always check for None before calling .group().

re.findall gets them all

import re

chat = """Aarav: my no is 9876543210
Sneha: mine is +91 8123456789, office 080-2345678
Kabir: PIN 560038, email [email protected]"""

print(re.findall(r"[6-9]\d{9}", chat))
print(re.findall(r"\b[1-9]\d{5}\b", chat))
print(re.findall(r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+", chat))

\b is a word boundary: it stops \d{6} from matching six digits from the middle of a phone number. (?:...) groups without capturing.

Groups pull out the parts

import re

m = re.search(r"([\w.]+)@([\w.]+)", "Write to [email protected] today")
print(m.group(0))
print(m.group(1), "|", m.group(2))

marks = "Riya: 87, Kabir: 92, Sneha: 78"
pairs = re.findall(r"(\w+): (\d+)", marks)
print(pairs)
print({name: int(score) for name, score in pairs})

group(0) is the whole match; group(1), group(2) are the brackets in order. With groups, findall returns tuples of the groups.

re.sub replaces

import re

print(re.sub(r"\s+", " ", "  Riya    Sharma   CSE ").strip())
print(re.sub(r"[6-9]\d{9}", "XXXXXXXXXX", "Call 9876543210 or 8123456789"))
print(re.sub(r"(\d{2})\d{6}(\d{2})", r"\1******\2", "Paid from 9876543210"))

In the replacement, \1 and \2 put the groups back. This is how apps show your number as 98******10.

Validating: search vs fullmatch

import re

pin = r"[1-9]\d{5}"
for text in ["560038", "056003", "5600381", "PIN 560038"]:
    print(f"{text:<11} search={bool(re.search(pin, text))!s:<5} fullmatch={bool(re.fullmatch(pin, text))}")

search is happy if the pattern appears anywhere, so "5600381" passes. To check that the whole text is a PIN, use re.fullmatch (or ^...$ with search).

Always use raw strings. In a normal string, "\b" is a backspace character, so re.search("\bcat\b", s) silently matches nothing. r"\bcat\b" passes the backslashes to re untouched. And remember . means any character: to match a real dot in gmail.com, write \..

Lab mapping. "Check whether a phone number / email is valid using regex" is a VTU Python lab program (its textbook, Automate the Boring Stuff, has a full regex chapter), and regex appears in the Anna, JNTU, AKTU and Mumbai syllabi as well.

Viva: "match vs search vs fullmatch?" (start only / anywhere / whole string) "What does findall return with groups?" (tuples) "Why r""?" "Greedy vs non-greedy?" (.* takes as much as it can, .*? as little).

In one line

Write regex as r"..."; search finds anywhere, fullmatch validates the whole text, findall lists all (tuples with groups), sub replaces (\1 reuses a group).