Skip to content
C++

std::regex Quick Reference

C++ regex cheat sheet — std::regex syntax, std::regex_search/match/replace, capture groups, iterators, and performance tips.

Quick Start

cpp
#include <regex>

std::string text = "Hello, World! 2025-01-15";
std::regex date_pattern{R"(\d{4}-\d{2}-\d{2})"};

// Check if pattern exists anywhere in text
bool found = std::regex_search(text, date_pattern);

// Full match (entire string must match)
bool exact = std::regex_match("2025-01-15", date_pattern);

// Extract matches
std::smatch match;
if (std::regex_search(text, match, date_pattern))
    std::println("date: {}", match[0].str());  // "2025-01-15"

std::regex Construction

cpp
// ECMAScript syntax (default)
std::regex re{R"(\d+\.\d+)"};

// Other syntaxes
std::regex posix{R"([0-9]+)", std::regex::extended};

// Flags (can combine with |)
std::regex ci{R"(hello)", std::regex::icase};          // case-insensitive
std::regex ml{R"(^start)", std::regex::multiline};     // ^ matches line start
std::regex ns{R"(.)", std::regex::ECMAScript};         // . matches \n too with dotall

// Precompile for reuse (expensive to construct — do it once)
const std::regex email_re{
    R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"
};

Pattern Syntax (ECMAScript)

cpp
. — any char except \n
\d — digit [0-9]
\D — non-digit
\w — word char [a-zA-Z0-9_]
\W — non-word
\s — whitespace [ \t\n\r\f\v]
\S — non-whitespace

^ — start of string (or line with multiline)
$ — end of string (or line with multiline)
\b — word boundary
\B — non-word boundary

* — 0 or more (greedy)
+ — 1 or more (greedy)
? — 0 or 1
{n} — exactly n
{n,} — n or more
{n,m} — between n and m
*? +? {n,m}? — lazy (non-greedy)

[abc] — character class
[^abc] — negated class
[a-z] — range
(abc) — capture group
(?:abc) — non-capturing group
a|b — alternation
\1 \2 — backreferences
(?=...) — positive lookahead
(?!...) — negative lookahead

Matching and Searching

cpp
std::string s = "price: $42.50 and $18.99";
std::regex price{R"(\$(\d+)\.(\d{2}))"};
std::smatch m;

// regex_match — whole string must match
std::regex_match("42.50", std::regex{R"(\d+\.\d+)"}); // true

// regex_search — find anywhere in string
if (std::regex_search(s, m, price)) {
    std::println("full: {}", m[0].str());  // "$42.50"
    std::println("dollars: {}", m[1].str()); // "42"
    std::println("cents: {}", m[2].str());   // "50"
    std::println("prefix: {}", m.prefix().str()); // "price: "
    std::println("suffix: {}", m.suffix().str()); // " and $18.99"
}

Capture Groups

cpp
std::string log = "2025-01-15 14:30:22 ERROR Something failed";
std::regex log_re{R"((\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.+))"};
std::smatch m;

if (std::regex_match(log, m, log_re)) {
    std::println("date: {}",    m[1].str());  // "2025-01-15"
    std::println("time: {}",    m[2].str());  // "14:30:22"
    std::println("level: {}",   m[3].str());  // "ERROR"
    std::println("message: {}", m[4].str());  // "Something failed"
}

// Named groups (C++11 ECMAScript doesn't support, but extension in some)
// Use numbered groups instead

Iterating All Matches

cpp
std::string text = "foo 42 bar 17 baz 99";
std::regex nums{R"(\d+)"};

// sregex_iterator
for (auto it = std::sregex_iterator(text.begin(), text.end(), nums);
     it != std::sregex_iterator{}; ++it) {
    std::println("{}", (*it)[0].str());  // 42, 17, 99
}

// sregex_token_iterator (iterate over captures or splits)
std::sregex_token_iterator words_it(text.begin(), text.end(), std::regex{R"(\s+)"}, -1);
std::sregex_token_iterator end;
for (; words_it != end; ++words_it)
    std::println("{}", words_it->str());  // "foo", "42", "bar", ...

Replace

cpp
std::string html = "<b>Hello</b> <i>World</i>";

// Replace all matches
std::string stripped = std::regex_replace(html, std::regex{R"(<[^>]+>)"}, "");
// "Hello World"

// Replace with capture group reference
std::string s = "John Smith, Jane Doe";
std::string result = std::regex_replace(s, std::regex{R"((\w+) (\w+))"},
                                         "$2, $1");
// "Smith, John, Doe, Jane" (replaces each match)

// Replace only first match
std::string first_only = std::regex_replace(s,
    std::regex{R"(\w+ \w+)"},
    "PERSON",
    std::regex_constants::format_first_only);

Split a String

cpp
std::string csv = "Alice,Bob,,Carol,Dave";
std::regex comma{","};

// Split into tokens (-1 means between matches)
std::sregex_token_iterator it(csv.begin(), csv.end(), comma, -1);
std::vector<std::string> parts(it, std::sregex_token_iterator{});
// {"Alice", "Bob", "", "Carol", "Dave"}

Error Handling

cpp
try {
    std::regex bad_re{"[unclosed"};
} catch (const std::regex_error& e) {
    std::println("regex error: {} (code {})", e.what(), (int)e.code());
}

Performance Tips

cpp
// 1. Compile the regex ONCE (construction is expensive)
// BAD: recompiles every call
void bad_check(const std::string& s) {
    if (std::regex_match(s, std::regex{R"(\d+)"})) { /* ... */ }
}

// GOOD: compile once
void good_check(const std::string& s) {
    static const std::regex re{R"(\d+)"};
    if (std::regex_match(s, re)) { /* ... */ }
}

// 2. Use raw strings to avoid double-escaping
std::regex path{R"(C:\\Users\\[^\\]+)"};   // without raw: "C:\\\\Users\\\\[^\\\\]+"

// 3. For simple patterns, use string methods instead
s.find("literal") != std::string::npos;   // faster than regex for plain strings
s.starts_with("prefix");                   // C++20

// 4. Consider alternatives for hot paths
// - re2 library: linear-time, no backtracking
// - PCRE2: full Perl-compatible regex, faster than std::regex
// - std::string_view::find + manual parsing: often fastest

std::regex vs Alternatives

std::regexre2PCRE2
C++ standardYes (C++11)ExternalExternal
Compile timeSlowSlowSlow
Match timeSlow (backtracking)Fast (linear)Fast
FeaturesECMAScript + othersLimitedFull Perl
LookaheadYesNoYes
Named groupsNo (ECMAScript, C++11)NoYes
RecommendedSimple scriptsProduction at scaleFull features
Edit on GitHub