Concepts / Regular Expression Patterns

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.

  • Programming

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?

  • Yes, it matches

  • No, it does not match

Reveal answer
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.

Callregex_search(line,matches, pat)Search line forpattern in patMatch found?Store results inmatchesPrint matches[0]Check matches.size()andmatches[1].matchedPrint submatchPrint nothing forsubmatchReturn false, if bodyskippedpasses line, matches, patyes, return trueno, return falseinside if bodysubmatch matchedsubmatch not matched
What is the sequence of steps from calling regex_search through the true or false return value to deciding whether a submatch is printed?

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.

matches[0]AB12345, the complete matchmatches[1]optional group, not matchedsubmatch follows the full match
After a successful regex_search, what does each index in matches hold, and how does the full match relate to the submatch stored after it?

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.

cpp
Output
AB12345
Notes on the code
  • 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_search returns true.
  • Only matches[0], the complete match, is printed.
Worked example

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?

  1. 1
    Line one

    The search finds AB12345: two letters AB, no space, and five digits 12345. The search returns true, so the if body runs.

  2. 2
    Print 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.

  3. 3
    Line 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.

Result

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.

\w{2}exactly two wordcharacters, the letters\s*any amount of whitespace,including none\d{5}exactly five digits(-\d{4})?optional group: dash thenfour digits, captured as asubmatch
Which part of the pattern string describes which characters, how many repetitions, and which group is captured as a submatch?

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.

Ttwo letters pieceXtwo letters piecespacewhitespace piece7five digits piece7five digits piece8five digits piece4five digits piece5five digits piece, matchends
Which characters in the input line does each part of the pattern consume, and where does matching stop?
Worked example

Locating Where a Pattern Fails

Trace the postal code pattern on the text TX 7784, which has only four digits after the space.

  1. 1
    Letters

    TX matches the two-letter piece, so far so good.

  2. 2
    Whitespace

    The single space matches the optional whitespace piece.

  3. 3
    Digits

    7784 supplies four of the five required digits. The five-digit piece needs one more digit, but the text has run out.

  4. 4
    Failure 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.

Result

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.

cpp
Notes on the code
  • Raw string: backslashes appear exactly as the pattern notation shows them.
cpp
Notes on the code
  • 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 like

    Printing matches[1] for the line Send to AB12345 now, where the optional dash group did not match.

    Why it fails

    The optional group may not participate. Its matched flag is false, so the program should not treat it as a captured value.

    Do this instead
    Check matches.size() and matches[1].matched before reading matches[1].
  • Common mistake

    Treating matches[1] as the complete match

    What it looks like

    Assuming that the first index after the call holds the full text, so printing matches[1] to show the postal code.

    Why it fails

    matches[0] is always the complete match when a search succeeds. Elements after it are sub-matches.

    Do this instead

    Print 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 like

    Writing the pattern as an ordinary string with a single backslash before w, instead of using a raw string or doubled backslashes.

    Why it fails

    The 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 instead

    Use the raw string literal R"(...)" so the pattern is written exactly as in regular expression references.

Predict and Check

MEDIUM

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.

Related concepts

More concepts starting with R