Alternation and Flags
Regular Expressions in Practice
Chapter 3 ยท Alternation and Flags
Two tools finish the basics. Alternation lets one pattern say "this or that". Flags change how the whole engine reads a pattern: ignoring case, letting ^ and $ work on every line, letting the dot cross line breaks, and allowing you to lay a long pattern out with spaces and comments.
Alternation with the Pipe
The pipe | means "match the left side, or else the right side". It has the lowest precedence of any operator, so it splits the whole pattern unless you fence it in with a group.
The first pattern also found the cat inside catalog. Wrapping the choice in (?:...) and adding \b word boundaries on both sides restricts it to whole words.
The pipe splits the whole pattern
Without parentheses, each side of the pipe is a complete alternative, including any anchors or text beside it.
When the choice is a single character, a class such as [ae] is shorter and faster than (?:a|e). Use the pipe when the alternatives are longer than one character.
Order matters
The engine tries alternatives left to right and takes the first one that lets the whole pattern succeed. It does not look for the longest. Put longer alternatives first when one is a prefix of another.
Flags
A flag is passed as a third argument (re.findall(pattern, text, re.I)) or, later, to re.compile. Combine several with the bitwise-or operator |. Each has a long name and a one-letter alias.
| Long name | Short | Effect |
|---|---|---|
re.IGNORECASE | re.I | letters match in either case |
re.MULTILINE | re.M | ^ and $ match at every line, not just the string ends |
re.DOTALL | re.S | . also matches the newline character |
re.VERBOSE | re.X | whitespace and # comments in the pattern are ignored |
IGNORECASE
The last line shows an inline flag: (?i) at the start of the pattern switches the flag on without an extra argument. The letters are i, m, s and x. Inline flags are handy when the pattern is stored somewhere that cannot carry a separate flags value.
MULTILINE
By default ^ matches only at the very start of the string and $ only at the very end (or just before a final newline). With re.M they match at the start and end of each line.
The first call finds nothing because ^\w+$ would need the whole string to be one word. Note that \w never matches a newline, so a match can never leak from one line into the next here.
DOTALL
The dot normally stops at a newline. re.S lets it cross, which matters when a single item spans several lines.
Be careful not to mix up the two line flags. MULTILINE changes the anchors; DOTALL changes the dot. They are independent and are often used together.
Combining flags
VERBOSE
A long pattern on one line is hard to read. re.X tells the engine to ignore whitespace and everything from a # to the end of the line, so you can lay a pattern out in parts and explain each part. Use a triple-quoted raw string.
The price of verbose mode is that spaces and # in the pattern are now ignored, so to match them literally you must escape them (\ , \#) or put them in a class ([ ], [#]).
re.I when the data's case is unreliable, re.M when you process text line by line as one string, re.S when one item crosses lines, and re.X for any pattern longer than a single short line.
Challenges
Given s = "The Cat sat; a DOG barked; cats and dogs; concat", find every whole word that is cat or dog, with an optional plural s, ignoring case. Print the list and its length. Then run the naive cat|dog with re.I and explain the difference.
Given a four-line log string (each line: a date, a level in mixed case, a message), use finditer with named groups date, level and msg, and the flags needed, to print a groupdict() for only the error and warn lines. Then show what findall returns for the same pattern when you leave out the flags, and say why.
Write a verbose pattern with comments that matches clock times such as 9:05, 14:30:59 and 9:05 PM: named groups h, m, optional s (non-capturing colon group) and optional ap, with leading and trailing space allowed and case ignored. Test it on "9:05", "14:30:59", "9:05 PM", "9:5" and "25:61x", and say why the last one does not match.
Quick Reference
- Alternation:
a|bhas the lowest precedence; fence it with(?: ... ) - Order: alternatives are tried left to right; put longer ones first
- Whole words:
\b(?:cat|dog)\b - re.I: ignore case; inline
(?i) - re.M:
^and$work per line - re.S:
.also matches a newline - re.X: ignore pattern whitespace and
#comments; escape or bracket literal spaces - Combine:
re.I | re.M
re.sub with backreferences and functions, and re.split.