Common Regex Mistakes
16 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)A wrong regex often still runs. It just matches too much, too little, or the wrong thing. These are the mistakes that appear most in real code and in interviews.
Forgetting the raw string
Raw string is a string written as r"...". Python keeps every backslash as typed, which is what regex patterns need.
In a normal string, "\b" is the backspace character, not a word edge. The pattern then never matches what you meant, and no error appears.
import re
print(re.search("\bcat\b", "a cat"))print(re.search(r"\bcat\b", "a cat"))Example 1 · Raw string or not
import re
print(len("\b"), len(r"\b"))print(re.findall("\bcat\b", "a cat sat"))print(re.findall(r"\bcat\b", "a cat sat"))1 2 [] ['cat']
What this code does
- The normal string
"\b"is one backspace character, so its length is 1. The raw string has 2 characters. - Only the raw pattern finds the word.
Always write r"..." for patterns. The first line prints None; the second finds the word.
Greedy quantifiers take too much
Greedy quantifier is a repeat such as + or * that matches as much text as possible and backs off only if needed. Adding ? after it makes it lazy, matching as little as possible.
By default + and * take as many characters as they can. In <b>x</b> the pattern <.+> runs to the last >. Add ? after the quantifier to take as few as possible.
import re
html = "<b>x</b>"print(re.search(r"<.+>", html).group())print(re.search(r"<.+?>", html).group())Example 2 · Greedy and lazy
import re
html = "<b>bold</b> and <i>italic</i>"print(re.findall(r"<.+>", html))print(re.findall(r"<.+?>", html))print(re.findall(r"<[^>]+>", html))['<b>bold</b> and <i>italic</i>'] ['<b>', '</b>', '<i>', '</i>'] ['<b>', '</b>', '<i>', '</i>']
What this code does
- The greedy pattern returns one long match.
- The lazy and the negated-class patterns return each tag.
A ? after a quantifier makes it lazy. A pattern such as <[^>]+> is another way and is often clearer.
Loose validation
To check that a whole text is a number, search is not enough: it accepts abc123xyz. Use fullmatch, or anchor with ^ and $. Careful: $ also matches just before a final newline.
import re
print(bool(re.search(r"\d+", "abc123")))print(bool(re.fullmatch(r"\d+", "abc123")))print(bool(re.search(r"^\d+$", "123\n")))Example 3 · search or fullmatch
import re
values = ["123", "12a", "", "007"]for v in values: print(repr(v), bool(re.search(r"\d+", v)), bool(re.fullmatch(r"\d+", v)))'123' True True '12a' True False '' False False '007' True True
What this code does
searchsays12ais fine because it contains digits.fullmatchaccepts only values made entirely of digits.
This prints True, False, True. fullmatch is the safest check for a whole value.
Special characters and the wrong tool
Escaping is putting a backslash before a special character so a pattern treats it as plain text. re.escape does this for a whole string.
A dot in your text is a wildcard in a pattern, so 1.5 also matches 1x5. Escape user input with re.escape. Regex cannot track nested structure, so use a real parser for HTML or JSON.
import re
term = "1.5"print(bool(re.search(term, "1x5")))print(bool(re.search(re.escape(term), "1x5")))Example 4 · Escape what the user typed
import re
text = "price (USD) is 5; price (USD) is 6"term = "price (USD)"print(len(re.findall(re.escape(term), text)))print(re.escape("a.b"))2 a\.b
What this code does
- Brackets are special in a pattern.
re.escapeturns them into plain text. - The count is 2, as the phrase appears twice.
Example 5 · The findall group trap
import re
sizes = "10px 20px 5em"print(re.findall(r"(\d+)px", sizes))print(re.findall(r"\d+px", sizes))['10', '20'] ['10px', '20px']
What this code does
- With a group,
findallreturns only the group, sopxis dropped. - Without the group it returns the whole match.
The first is True by mistake. The escaped pattern is False, as intended.
Next step
Check your understanding
11 quick questions