login
Holden Web
What you'll need to know tomorrow

Basic Regular Expressions

Suppose you've been given a big block of text and told you need to pull all of the US-style phone numbers from it. Writing a program like that requires breaking up all the words via the String split() method, then writing code to make sure that the numbers and dashes are all in the right places. We are talking about at least a dozen lines of code, and that doesn't even begin to account for special cases, like when the area code is in parentheses.

What if there was a special syntax so that you could find those numbers with a single line of code? Something like xxx-xxx-xxxx or (xxx) xxx-xxxx that you could apply to the text? The "x" would mean "any number," and the pattern would be applied and would return a list.

Fortunately, there is: Python lets you use regular expressions, which do that and much more besides! They aren't the answer to every string-related problem, but regular expressions are an important part of any developer's toolkit. This lesson will go over the basics of what you can do with regular expressions and will be followed by a more complete exposition of the capabilities of the re module.

In the 1950s, mathematician Stephen Cole Kleene described automata theory and formal language theory in a set of models using a notation called regular sets as a method to do pattern matching. Active usage of this system, called Regular Expressions, started in the 1960s and continued under such pioneers as David J. Farber, Ralph E. Griswold, Ivan P. Polonsky, Ken Thompson, and Henry Spencer.

Regular expressions, also called res or regexes, provide a concise and flexible means for matching strings of text. They are a common programming tool used not just in Python but many languages in common use today.

This lesson includes these sections:

Matching and Searching

The re module provides features to enable pattern matching in Python. The basic mode of operation is to call either the match() or search() function from that module with a regex as the first argument, and a string to match against as the second argument. If the regex matches the string, the module returns a match object, and analysis of the match object can give you information about (for example) the exact strings matched by various portions of the pattern.

Code and output
>>> import re
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m
<re.Match object; span=(0, 12), match='Isaac Newton'>
>>> m.groups()
('Isaac', 'Newton')
>>> m.group(0)
'Isaac Newton'
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
>>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
>>> m.group(1)                        # Returns only the last match.
'c3'
>>> m = re.search("\\d+", "hello123extra")
>>> m.start()
5
>>> m.end()
8
>>> m.span()
(5, 8)
>>> m.group(0)
'123'
>>>
Modern Python Match objects now display as <re.Match object; span=(0, 12), match='Isaac Newton'>. The original lesson showed the older <_sre.SRE_Match object at 0x...> form; the output above has been regenerated on Python 3.14.

In the interactive session above, we used the re module's match() and search() functions to determine whether strings conformed to a specific pattern (provided as the first argument to the function call). match() requires the pattern to occur at the start of the target string, while search() will move through the target string looking for it. If the pattern is not present in the string, the function call returns None. Otherwise it returns a match object ("m," above) that can be queried for specific aspects of the matched string by calling its various methods.

Finding Characters: Regular Expression Patterns

A regular expression pattern is a way of describing a set of character strings. These descriptions can be relatively concise: the pattern "x" matches precisely one character, the lowercase letter "x." Some characters have special meanings, so the pattern "x+" matches any string of one or more lower case "x"s—the plus sign generates a more complex pattern from the pattern it follows.

Most characters can be used in patterns like the lower case "x" to "stand for themselves," so for example if you wanted to match the literal string "thing" you would do so with a pattern that reads "thing"—the "t" in the pattern matches a "t" in the string, and so on. But there are quite a few abbreviations: for example, '\d' matches any decimal digit (making it equivalent to the pattern "[0123456789]," as we will learn shortly). Here are some of the more common abbreviations.

Pattern StringDescription
.Matches any character except a newline in the target string.
^Matches the start of the target string, or the start of a line within the target string.
$Matches the end of the string, or just before the end of a line within the string. foo matches both "foo" and "foobar," while the regular expression foo$ matches only "foo."
*Matches the regex it follows, zero or more times, so ab* will match "a," "ab," or "a" followed by any number of "b"s.
+Matches the regex it follows, one or more times, so ab+ will match "a" followed by any number of "b"s, but will not match "a" alone.
?Optionally matches an occurrence of the regex that precedes it. ab? matches either of "a" or "ab."
\Matches special characters literally, allowing you to match plus signs, asterisks and other characters having special significance in regexes. Also introduces a special sequence such as '\d' to match any digit.
{m} (where m is an integer)Matches exactly m occurrences of the regex it follows.
{m,n} (where m and n are integers)Matches between m and n occurrences of the regex it follows.
[...]Matches any one of the set of characters appearing between the brackets. Special characters do not have their usual significance inside brackets, so [abc$] matches any of "a," "b," "c" or "$." A dash (-) between two characters specifies a range, so [a-z] matches any lower-case character.
[^...]Matches any character except one of the set appearing after the caret between the brackets. Note that the caret only has this special meaning when it immediately follows the opening left bracket.
|Alternation. A|B, where A and B are any regexes, first tries to match A and, if that fails, tries to match B. Any number of regexes can be used as alternates in this way, not just two.
(...)Groups a number of regexes together, usually for the purpose of treating them as a single element (for example, to use as an alternate with |). When a match object is created, the string matched by the parenthesized group is available using methods of the match object.
Tip There are many regex references available on the Internet; you might want to find and bookmark one or two of them!
Grouping in Patterns

As the last line above indicates, patterns can contain groups, indicated by parentheses. The strings matched by the groups are, under certain circumstances, available—again, by calling the match object's methods. The groups can be numbered (according to their relative positions in the pattern, and starting at one rather than Python's usual zero—group 0 refers to the match as a whole) and they can also be named if the group's opening parenthesis in the pattern is followed by a question mark, an upper case "P" and a name in angle brackets, as we saw with "(?P<first_name>\w+)" in the earlier interactive session.

Groupings in the pattern are the principal way of extracting required information from the match object. Strings matched by non-grouping portions of the pattern cannot be individually identified in the match object. When you are testing a new regular expression, it is often useful to interactively inspect the result of calling the match objects' groups() and groupdict() methods to verify that your pattern is matching as you expect.

When testing patterns, you can test for equality with those objects; but you should also remember to test that unacceptable strings are not, in fact, matched. This will usually involve the use of your test case's assertNone() method on the match result.

Substitution for Patterns

Besides the match() and search() functions, the re module provides functions that allow you to make replacements of patterns in the target string (these functions return new strings, of course, because strings are immutable in Python). The re.sub() function takes not only a pattern and a target string but also a replacement string, as shown below.

re.sub() Syntax
re.sub(pat, replacement, target[, count, flags])

This replaces each non-overlapping occurrence of the given pattern in the target string with the replacement element given as the third argument. If replacement is a string, any backslash escapes in it are processed down to individual characters (so, for example, "\n" is replaced by a newline character). Escapes of the form \n (where n is a decimal digit) allow replacement by one of the matched groups from the pattern. The replacement argument can also be a function, in which case it is called for each replacement with a single argument, which is the match object corresponding to the currently matched string that is to be replaced.

The optional argument count is the maximum number of pattern occurrences to be replaced; count must be a non-negative integer. If omitted or zero, all occurrences will be replaced. Empty matches for the pattern are replaced only when not adjacent to a previous match, so sub('x*', '-', 'abc') returns '-a-b-c-'. The flags, if present, are the usual regular expression matching flags, which we'll discuss a little later. Let's get an idea of what you can do with the replacement facilities.

This example simply shows straight pattern replacement: both calls to re.sub() replace all occurrences of the string "1" with a newline character. When the pattern contains characters like newline (normally represented by escape sequences), or special patterns (which also require backslashes), r(aw) strings can make patterns more readable and easier to type. The more complex the patterns become, the truer this is.

Code and output
>>> import re
>>> re.sub("1", "\\n", "123123123123") # replace digit one with newline
'\n23\n23\n23\n23'
>>> re.sub("1", r"\n", "123123123123") # replace digit one with newline
'\n23\n23\n23\n23'
Modern Python Prefer raw strings (r"...") for all regex patterns. Plain string patterns that rely on unrecognised backslash sequences (such as "\d" or "\+") now raise a SyntaxWarning in Python 3.12+ and will become errors in a future release. The session above deliberately contrasts "\\n" and r"\n"; in practice, reach for raw strings when things get complicated.

The next call to re.sub() uses a function to supply the replacement string: if the function is replacing a single minus sign it returns a space, but two minus signs are translated into a plus sign.

Code and output
>>> def dashrepl(matchobj):
...     if matchobj.group(0) == "-": return " "
...     else: return "+"
...
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro++gram files'

The next example finds the "#" marker and removes it and everything after it, then removes everything but the digits from the remaining string.

Code and output
>>> s = "(123) 456-7890 # Commented phone number"
>>> nocomment = re.sub("#.*$", "", s)
>>> nocomment
'(123) 456-7890 '
>>> re.sub(r"\D", "", nocomment)
'1234567890'

These examples attempt to match any string beginning and ending in an at sign ("@") with zero or more sequences of "=+=" in the middle (the "+" must be escaped to make the matching code treat it as an ordinary character).

Code and output
>>> re.sub("@(=+=)*@", "xxx", "@@")
'xxx'
>>> re.sub("@(=+=)*@", "xxx", "@=+=@")
'xxx'
>>> re.sub("@(=+=)*@", "xxx", "@=+==+=@")
'xxx'
>>> re.sub("@(=+=)*@", "xxx", "@=+=+=@")
'@=+=+=@'

The last example shows a pattern (a single vowel) being used to make many replacements—all vowels in the target string are replaced with a dash.

Code and output
>>> re.sub("[aeiouAEIOU]", "-", "The Quick Brown Fox Jumps Over the Lazy Dog")
'Th- Q--ck Br-wn F-x J-mps -v-r th- L-zy D-g'
>>>
Trying Out Patterns

It's useful to be able to try out lots of patterns as you are learning how they are made up. See if you can understand the following patterns by trying them against various strings. To help you do that, we'll write a little program that allows you to see the results of searching and matching for a specific pattern against a number of strings. The program reads a pattern, and if it's the empty string, terminates. Otherwise, it reads target strings and applies matches and searches on the strings that are subsequently input until the user enters an empty string, in which case it goes back to requesting a new pattern. Create pattest.py as shown:

Code
"""
pattest.py: Allows the checking of various patterns and target strings
"""
import re
while True:
    pat = input("Pattern: ")
    if not pat:
        break
    while True:
        s = input("Target : ")
        if not s:
            break
        mm = re.match(pat, s)
        if mm:
            print("Match : matched {0!r}".format(s[mm.start():mm.end()]))
            print("Match : groups:", mm.groups())
            print("Match : gdict :", mm.groupdict())
        else:
            print("Match : no match")
        ms = re.search(pat, s)
        if ms:
            print("Search: matched {0!r}".format(s[ms.start():ms.end()]))
            print("Search: groups:", ms.groups())
            print("Search: gdict :", ms.groupdict())
        else:
            print("Search: no match")

This lets you test many strings against the same pattern quite quickly. Run it and ensure that you can think of strings that both match and don't match the patterns given below.

PatternDescription
[0123456789]+Matches one or more decimal digits.
[\d]+Same as above. Remember to verify that some strings don't match the pattern -
[\w]+ +[\w]+Matches two words separated by any number of spaces.
\(\d\d\d\) \d\d\d-\d\d\d\dMatches a US telephone number with parentheses around the area code and a dash between the exchange and the number.
home-?brewThere should be exactly two strings that match this pattern.
\$\d+(.\d{2})?An amount of money (in dollars) with optional cents.

We've made a start on the use of regular expressions. While they aren't the answer to every problem, they can help to solve tricky text recognition problems. Just don't treat them as the first weapon in your arsenal—the string methods were provided for a reason! In the next lesson, we'll expand our knowledge of regular expressions further.