Menu

Golang Regexp: Match, Find, Submatches and Replace

How to use regular expressions in Go with the regexp package: MustCompile, MatchString, FindString and FindAllString, capture groups and named groups, ReplaceAllString, and the RE2 syntax limits such as no lookbehind.

This page includes runnable editors - edit, run, and see output instantly.

Compile once, then match

Write patterns as raw string literals in backticks. In a normal double-quoted string every backslash must be doubled ("\\d+"), which quickly becomes unreadable.

regexp.MustCompile panics if the pattern is invalid, which is what you want for a pattern in your source code: a typo fails immediately at startup, not on the first request. For patterns that come from users or config, use regexp.Compile and handle the error:

re, err := regexp.Compile(userPattern)
if err != nil {
	return fmt.Errorf("bad pattern: %w", err)
}

Compile once. Compiling is far more expensive than matching. A regexp.MustCompile inside a function that runs per request or per line repeats that work every time. Put the compiled *regexp.Regexp in a package-level var or a struct field. A compiled regexp is safe to use from many goroutines at once.

regexp.MatchString(pattern, s) compiles and matches in one call. It is fine for a one-off check and wasteful in a loop.

Finding matches

The method names follow a pattern: Find + All? + String? + Submatch? + Index?.

PartMeaning
Allevery non-overlapping match; takes a limit n (-1 for all)
Stringworks on a string; without it, the method takes and returns []byte
Submatchalso returns the capture groups
Indexreturns byte offsets instead of text

FindString returns "" when there is no match, which is indistinguishable from matching an empty string. When the pattern can match empty text, use FindStringIndex (returns nil for no match) or MatchString first. The All variants return nil when nothing matches.

Capture groups

Parentheses capture. FindStringSubmatch returns the whole match at index 0, then one entry per group:

Always check for nil before indexing the result; on no match, m[1] panics.

(?:...) groups without capturing, for alternation or repetition: (?:ab)+. Named groups are written (?P<name>...), and since Go 1.22 also (?<name>...).

Replacing

A classic trap with group references: "$1x" is read as a group named 1x, which does not exist, and expands to an empty string. Write "${1}x".

For a fixed string, strings.ReplaceAll, strings.Contains and strings.Split are simpler and faster than a regexp. Reach for regexp when the text you are looking for has a shape rather than a fixed value.

Syntax quick reference

Go uses RE2 syntax, which covers the familiar Perl-style features:

SyntaxMatches
.any character except newline (with (?s), newline too)
\d \w \sdigit, word character [0-9A-Za-z_], whitespace (ASCII only)
\D \W \Sthe negations
[abc] [^abc] [a-z]character classes
\pL \p{Greek}Unicode classes: any letter, any Greek character
* + ? {n,m}repetition, greedy
*? +? ??repetition, lazy
^ $start and end of text (of line, with (?m))
\bword boundary (ASCII)
a|balternation
(?i)case-insensitive from that point to the end of the enclosing group; usually written at the start of the pattern

\d and \w only match ASCII. For "any letter in any language", use \pL, and for any Unicode digit, \p{Nd}. regexp.QuoteMeta(s) escapes every special character in s, which you need when building a pattern from user input.

What RE2 cannot do

Go's regexp guarantees that matching takes time linear in the length of the input. Features that would break that guarantee are not supported:

  • No lookahead or lookbehind: (?=...), (?!...), (?<=...), (?<!...) are compile errors. Because Go 1.22 started accepting (?<name>...) for named groups, a lookbehind now fails with the confusing message invalid named capture, as the program below shows.
  • No backreferences: (\w)\1 to match a doubled letter is not possible.
  • No possessive quantifiers or atomic groups.

The workaround is almost always the same: capture a little more than you need, then filter or slice in Go. The payoff is that a Go regexp cannot be pushed into catastrophic backtracking. A pattern like (a+)+$ that can freeze a PCRE engine on a short input runs in linear time here, which matters when the pattern or the input comes from outside.

Common mistakes

  • Compiling inside a loop or handler. Compile once into a package-level variable.
  • Double-quoted patterns. "\d" is not even a valid Go string; use backticks.
  • Forgetting anchors in validation. \d{5} matches inside "abc123456xyz". Use ^\d{5}$.
  • Indexing a submatch result without a nil check.
  • Expecting \w or \b to understand non-ASCII text. Use Unicode classes like \pL.
  • Using regexp for fixed strings. The strings package is clearer and faster.

Frequently Asked Questions

How do I check if a string matches a regex in Go?

Compile the pattern once with regexp.MustCompile, writing it as a raw string in backticks so backslashes need no escaping, then call re.MatchString(s). Without anchors (^ and $), MatchString returns true if the pattern matches anywhere in the string.

Does Go regexp support lookahead and lookbehind?

No. Go's regexp uses RE2 syntax, which has no lookahead, lookbehind or backreferences. In exchange, matching runs in time linear in the input size, so a hostile pattern or input cannot hang your program. Use capture groups and a little Go code instead, or split the check into two regexes.

What is the difference between regexp.Compile and regexp.MustCompile?

Compile returns the compiled pattern and an error. MustCompile panics on an invalid pattern instead. Use MustCompile for patterns written in your source code, typically in a package-level variable, so a typo fails at startup. Use Compile for patterns that come from users or config files.

How do I get capture groups from a regex in Go?

Use FindStringSubmatch, which returns a slice where index 0 is the whole match and index 1, 2, ... are the groups, or nil if there is no match. For all matches use FindAllStringSubmatch(s, -1). Named groups (?P<year>\d{4}) can be looked up with re.SubexpIndex("year").

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED