Regular Expressions (std::regex)
Pattern matching baked into the standard library — match, search, capture, and replace, without a third-party dependency.
Intermediate
<regex> gives you a pattern type (std::regex, compiled
once from a pattern string) and three algorithms that use it:
std::regex_match, std::regex_search, and
std::regex_replace. The default pattern grammar is ECMAScript — the same
flavor JavaScript's regex syntax uses — unless you pass a different flag to the
std::regex constructor.
| Type/function | What it's for |
|---|---|
std::regex | a compiled pattern — construct it once, reuse it |
std::smatch | match results against a std::string — holds the whole match plus any capturing groups |
std::regex_match | does the entire string match the pattern? |
std::regex_search | does the pattern match anywhere in the string? |
std::regex_replace | return a new string with every match replaced |
regex_match requires the pattern to account for
the whole string (implicitly anchored at both ends), while
regex_search is happy to find the pattern as a substring anywhere.
std::regex digits(R"(\d+)"); // raw string literal — avoids escaping the backslash as \\d
std::regex_match("12345", digits); // true — the ENTIRE string is digits
std::regex_match("abc123", digits); // false — "abc" doesn't match, and regex_match needs the whole string
std::regex_search("abc123", digits); // true — \d+ matches the "123" substring somewhere in the string
R"(...)" — without it, a pattern like
\d would need to be written "\\d", since \d alone isn't
a valid C++ string escape. Raw strings sidestep the double-escaping entirely and are worth
defaulting to for any nontrivial pattern.
std::smatch gives
you indexed access to each one after a successful match — index 0 is always the entire
match, index 1 is the first parenthesized group, and so on.
std::string line = "2026-08-09: build succeeded";
std::regex entry(R"((\d{4}-\d{2}-\d{2}): (.+))");
std::smatch m;
if (std::regex_match(line, m, entry)) {
std::cout << "date: " << m[1] << "\n"; // 2026-08-09
std::cout << "message: " << m[2] << "\n"; // build succeeded
}
regex_replace takes a replacement string that can reference captured groups
with $1, $2, etc. — useful for reformatting text without
manually re-assembling it from smatch pieces.
std::string date = "2026-08-09";
std::regex iso(R"((\d{4})-(\d{2})-(\d{2}))");
std::string us = std::regex_replace(date, iso, "$2/$3/$1"); // "08/09/2026"
std::regex compiles the pattern into an internal
representation — that compilation is the expensive part, not the actual matching. If
the same pattern is applied to many strings (parsing every line of a log file, say),
construct the std::regex once outside the loop and reuse it, rather than
rebuilding it from the pattern string on every iteration.
std::regex pattern(R"(\d+)"); // compiled ONCE
for (const auto &line : manyLines) {
if (std::regex_search(line, pattern)) { /* ... */ } // reuses the already-compiled pattern
}
regex_replace together, pulling structured fields out of
a few log-style lines and reformatting one of them:
TopNotchNote/cpp/regex_log_parser_demo.cpp