Regular Expression Patterns
regex_search(line, matches, pat) searches a line for the pattern stored in pat, stores the results in matches, and returns false when no match is found.
Why Programs Search Text
Searching text for a pattern is one of the most common jobs a program does. A postal code, an order number, or an identifier can all be described by a pattern rather than by a fixed list of strings. To work with such a pattern, a program needs two things: a way to test whether a line of text contains the pattern, and a way to read back exactly what was matched. In C++, both jobs are handled by the function regex_search together with an object of type smatch.
Regular expressions describe the shape of text in a short, terse notation. Instead of listing every string you want to find, you write one pattern, and a search tool looks for text with that shape. The standard library supports this notation through the std::regex class and its supporting functions, and the rest of this article builds up a single working example: a pattern that recognizes a U.S. postal code of the form XXddddd-dddd and its variants.
What do you think happens?
The postal code pattern asks for two letters, optional whitespace, and five digits. Does it accept the text TX 77845?
Reveal answer
Yes, it matches
TX supplies the two letters, the single space supplies the optional whitespace, and 77845 supplies the five digits. The optional dash-and-four-digits group is simply absent, which is allowed because that group is optional.
Reading a Pattern From Left to Right
A pattern is read from left to right, and each piece describes a stretch of text that the match must cover. The postal code pattern has four pieces. The first piece requires two word characters, which the source describes as two letters. The second piece allows any amount of whitespace, including none. The third piece requires exactly five digits. The last piece is a group that is optional: it allows a dash followed by four digits, or nothing at all.
Keep one question in mind while reading any pattern: what does this pattern accept, and at what point would a candidate string stop matching? The pattern is itself just a string. The std::regex object takes that string and turns it into a pattern that can be used for matching. Each quantifier controls how many times a piece may repeat, and the question mark makes a piece optional.
The Three Arguments of regex_search
The call regex_search(line, matches, pat) has three parts, and each has a distinct job. The first argument, line, is the text to be searched. The third argument, pat, holds the regular expression, which is the description of what to look for. The second argument, matches, is where the results are stored if a match is found.
The function searches the line for anything that matches the pattern stored in pat. If it finds a match, it stores the match information in matches. If no match is found, the function returns false. This return value is what makes an if statement work: the body runs only when the search has succeeded. The flow below shows how a single line travels from the call to the decision about printing.
What the smatch Holds
The matches variable has the type smatch. The letter s stands for sub or string, and an smatch is a vector of sub-matches, where each element has the type string. A vector is an ordered collection, so each sub-match can be reached by its position. This is why the code writes matches[0] to access one element and matches.size() to ask how many elements there are.
The first element, matches[0], is the complete match: the full piece of text that the pattern matched. The elements after it are sub-matches, which are the parts captured by parentheses in the pattern. In the postal code pattern, the parenthesized group is optional, so it may or may not take part in a given match. A learner reading the results should think of matches as a list whose first item is the whole match and whose later items are the captured parts.
Because the optional group may not participate, the program should check matches.size() and matches[1].matched before reading a submatch. The diagram below shows the state of matches after a successful search on the line Send to AB12345 now, where the optional dash group does not appear.
Tracing a Two-Line Input
Consider a small program that reads a file one line at a time. For each line, it searches for the postal code pattern and prints the match. The pattern is defined before the loop, each line is read in turn, and a result is printed only when the search succeeds.
AB12345- The pattern is defined once, before the loop.
- Each iteration reads one line and creates a fresh smatch.
- The if statement runs the body only when
regex_searchreturns true. - Only matches[0], the complete match, is printed.
Predicting Output for Two Lines
The input has two lines. Line one is Send to AB12345 now. Line two is no code here. Which lines are printed, and what is printed?
- 1Line one
The search finds AB12345: two letters AB, no space, and five digits 12345. The search returns true, so the if body runs.
- 2Print the complete match
The program prints matches[0], which is AB12345. The optional dash group did not take part, so there is no submatch to print.
- 3Line two
The line no code here contains no run of five digits. The search returns false, so the if body is skipped and nothing is printed.
Only AB12345 is printed. The second line produces no output.
Anatomy of the Postal Code Pattern
The pattern is stored in a regex object and declared as regex pat with a raw string literal. Its text, written in the notation you will see in regular expression references, reads as two letters, optional space, five digits, and an optional dash with four digits. Each token below contributes one part of that reading.
The quantifiers {2} and {5} set exact counts, while the star in \s* allows zero or more and the question mark after the group makes the whole group optional. The parentheses do two jobs at once: they group the dash and digits together, and they make that group capturable, which is why it can appear as matches[1].
Matching the Postal Code Pattern Position by Position
Now trace the pattern across the text TX 77845. The first two positions are consumed by the two-letter piece, the third position by the optional whitespace piece, and the last five positions by the five-digit piece. Because the optional group does not appear in this text, it contributes nothing, and the match ends after the fifth digit.
Locating Where a Pattern Fails
Trace the postal code pattern on the text TX 7784, which has only four digits after the space.
- 1Letters
TX matches the two-letter piece, so far so good.
- 2Whitespace
The single space matches the optional whitespace piece.
- 3Digits
7784 supplies four of the five required digits. The five-digit piece needs one more digit, but the text has run out.
- 4Failure point
Matching stops at the end of the text, because the five-digit piece cannot be satisfied. The optional group cannot rescue the match, because it begins with a dash, which is not present.
No match is found, so regex_search returns false and the if body is skipped.
Raw Strings Versus Escaped Strings
A raw string literal starts with R" followed by an opening parenthesis, and ends with a closing parenthesis followed by a quote. Inside it, backslashes and quotes are taken exactly as written, so they do not need to be escaped. Patterns tend to contain many backslashes, which is why raw strings are particularly suitable for regular expressions. The pattern appears in the definition in the same form you would write it on paper.
- Raw string: backslashes appear exactly as the pattern notation shows them.
- Ordinary string: every backslash must be doubled.
- Both versions define the same pattern; only the literal's spelling differs.
Both versions define the same pattern. The conventional form forces every backslash to be doubled, which makes the pattern harder to read and easier to mistype. The raw form keeps the pattern in the notation used in regular expression references, so the text you read in the code is the text that describes the matching.
Three Mistakes When Reading matches
Most errors with patterns come from confusing the string syntax with the pattern syntax, and from misreading the smatch that regex_search fills in. The three mistakes below follow directly from the material in this article.
Common mistake Reading a submatch without checking that it took part in the match
What it looks likePrinting matches[1] for the line Send to AB12345 now, where the optional dash group did not match.
Why it failsThe optional group may not participate. Its matched flag is false, so the program should not treat it as a captured value.
Do this insteadCheck matches.size() and matches[1].matched before reading matches[1].Common mistake Treating matches[1] as the complete match
What it looks likeAssuming that the first index after the call holds the full text, so printing matches[1] to show the postal code.
Why it failsmatches[0] is always the complete match when a search succeeds. Elements after it are sub-matches.
Do this insteadPrint matches[0] for the full match, and use later indices only for captured parts.
Common mistake Writing a pattern in an ordinary string without doubling its backslashes
What it looks likeWriting the pattern as an ordinary string with a single backslash before w, instead of using a raw string or doubled backslashes.
Why it failsThe string syntax and the pattern syntax are different layers. A single backslash in an ordinary string is interpreted by C++ before the regex is ever built.
Do this insteadUse the raw string literal R"(...)" so the pattern is written exactly as in regular expression references.
Predict and Check
Using only the postal code pattern, decide whether the line Ship to TX 77845-1234 produces printed output. If it does, state what matches[0] holds and whether the optional group contributes a submatch.
Hints
- Look for two letters, optional whitespace, and five digits in the order they appear.
- The optional group begins with a dash and must be followed by exactly four digits.
Key Takeaways
regex_search(line, matches, pat)searches a line for the pattern in pat, stores the results in matches, and returns false when no match is found.- An smatch is a vector of string sub-matches. matches[0] is always the complete match when a search succeeds, and later elements are captured sub-matches.
- Check
matches.size()and matches[1].matched before reading a submatch, because an optional group may not take part in a match. - A regular expression pattern is read left to right, with each piece and quantifier describing a stretch of text the match must cover. A raw string literal R"(...)" lets you write that pattern exactly as it appears in the notation.