Menu

Golang Regexp: Match, Find, Submatches und Replace

So nutzt du reguläre Ausdrücke in Go mit dem Paket regexp: MustCompile, MatchString, FindString und FindAllString, Capture Groups und benannte Gruppen, ReplaceAllString und die Grenzen der RE2-Syntax wie das fehlende Lookbehind.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Einmal kompilieren, dann matchen

Schreib Muster als Raw-String-Literale in Backticks. In einem normalen String in doppelten Anführungszeichen muss jeder Backslash verdoppelt werden ("\\d+"), was schnell unlesbar wird.

regexp.MustCompile löst bei einem ungültigen Muster eine Panic aus, und genau das willst du bei einem Muster in deinem Quelltext: Ein Tippfehler fällt sofort beim Start auf, nicht erst beim ersten Request. Für Muster von Nutzern oder aus der Konfiguration nimm regexp.Compile und behandle den Fehler:

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

Einmal kompilieren. Kompilieren ist weit teurer als Matchen. Ein regexp.MustCompile in einer Funktion, die pro Request oder pro Zeile läuft, wiederholt diese Arbeit jedes Mal. Leg den kompilierten *regexp.Regexp in ein var auf Paketebene oder in ein Struct-Feld. Ein kompilierter Regexp kann gefahrlos von vielen Goroutinen gleichzeitig benutzt werden.

regexp.MatchString(pattern, s) kompiliert und matcht in einem Aufruf. Für eine einmalige Prüfung ist das in Ordnung, in einer Schleife ist es Verschwendung.

Treffer finden

Die Methodennamen folgen einem Muster: Find + All? + String? + Submatch? + Index?.

TeilBedeutung
Alljeder nicht überlappende Treffer; nimmt ein Limit n (-1 für alle)
Stringarbeitet auf einem string; ohne diesen Teil nimmt und liefert die Methode []byte
Submatchliefert zusätzlich die Capture Groups
Indexliefert Byte-Offsets statt Text

FindString gibt "" zurück, wenn es keinen Treffer gibt, und das ist nicht von einem Treffer auf einen leeren String zu unterscheiden. Kann das Muster leeren Text treffen, nimm FindStringIndex (gibt ohne Treffer nil zurück) oder vorher MatchString. Die All-Varianten geben nil zurück, wenn nichts passt.

Capture Groups

Klammern fangen ein. FindStringSubmatch liefert den gesamten Treffer an Index 0, dann einen Eintrag pro Gruppe:

Prüf immer auf nil, bevor du das Ergebnis indizierst; ohne Treffer löst m[1] eine Panic aus.

(?:...) gruppiert, ohne einzufangen, für Alternativen oder Wiederholungen: (?:ab)+. Benannte Gruppen schreibst du (?P<name>...), seit Go 1.22 auch (?<name>...).

Ersetzen

Eine klassische Falle bei Gruppenverweisen: "$1x" wird als Gruppe mit dem Namen 1x gelesen, die es nicht gibt, und wird zu einem leeren String expandiert. Schreib "${1}x".

Für einen festen String sind strings.ReplaceAll, strings.Contains und strings.Split einfacher und schneller als ein Regexp. Greif zu regexp, wenn der gesuchte Text eine Form hat statt eines festen Werts.

Syntax im Überblick

Go verwendet die RE2-Syntax, die die vertrauten Features im Perl-Stil abdeckt:

SyntaxTrifft
.jedes Zeichen außer Zeilenumbruch (mit (?s) auch den)
\d \w \sZiffer, Wortzeichen [0-9A-Za-z_], Leerraum (nur ASCII)
\D \W \Sdie Negationen
[abc] [^abc] [a-z]Zeichenklassen
\pL \p{Greek}Unicode-Klassen: jeder Buchstabe, jedes griechische Zeichen
* + ? {n,m}Wiederholung, gierig
*? +? ??Wiederholung, genügsam
^ $Anfang und Ende des Texts (mit (?m) der Zeile)
\bWortgrenze (ASCII)
a|bAlternative
(?i)ignoriert Groß- und Kleinschreibung ab dieser Stelle bis zum Ende der umschließenden Gruppe; meist an den Anfang des Musters geschrieben

\d und \w treffen nur ASCII. Für „jeden Buchstaben in jeder Sprache“ nimm \pL und für jede Unicode-Ziffer \p{Nd}. regexp.QuoteMeta(s) escapt jedes Sonderzeichen in s; das brauchst du, wenn du ein Muster aus Nutzereingaben baust.

Was RE2 nicht kann

Der Regexp von Go garantiert, dass das Matching in linearer Zeit zur Länge der Eingabe läuft. Features, die diese Garantie brechen würden, werden nicht unterstützt:

  • Kein Lookahead oder Lookbehind: (?=...), (?!...), (?<=...), (?<!...) sind Compilerfehler. Weil Go 1.22 begonnen hat, (?<name>...) für benannte Gruppen zu akzeptieren, scheitert ein Lookbehind jetzt mit der verwirrenden Meldung invalid named capture, wie das Programm unten zeigt.
  • Keine Rückverweise: (\w)\1, um einen doppelten Buchstaben zu treffen, ist nicht möglich.
  • Keine possessiven Quantoren oder atomaren Gruppen.

Der Workaround ist fast immer derselbe: Fang etwas mehr ein, als du brauchst, und filtere oder schneide dann in Go. Der Gewinn: Ein Go-Regexp lässt sich nicht in katastrophales Backtracking treiben. Ein Muster wie (a+)+$, das eine PCRE-Engine schon bei einer kurzen Eingabe einfrieren kann, läuft hier in linearer Zeit. Das zählt, wenn das Muster oder die Eingabe von außen kommt.

Häufige Fehler

  • In einer Schleife oder einem Handler kompilieren. Kompiliere einmal in eine Variable auf Paketebene.
  • Muster in doppelten Anführungszeichen. "\d" ist nicht einmal ein gültiger Go-String; nimm Backticks.
  • Anker bei der Validierung vergessen. \d{5} trifft innerhalb von "abc123456xyz". Nimm ^\d{5}$.
  • Ein Submatch-Ergebnis ohne Prüfung auf nil indizieren.
  • Erwarten, dass \w oder \b Text außerhalb von ASCII verstehen. Nimm Unicode-Klassen wie \pL.
  • regexp für feste Strings nutzen. Das Paket strings ist klarer und schneller.

Häufig gestellte Fragen

Wie prüfe ich in Go, ob ein String zu einem Regex passt?

Kompiliere das Muster einmal mit regexp.MustCompile und schreib es als Raw String in Backticks, damit Backslashes nicht escapt werden müssen. Ruf dann re.MatchString(s) auf. Ohne Anker (^ und $) gibt MatchString true zurück, wenn das Muster irgendwo im String passt.

Unterstützt Go regexp Lookahead und Lookbehind?

Nein. Das regexp von Go verwendet die RE2-Syntax, die weder Lookahead noch Lookbehind noch Rückverweise kennt. Im Gegenzug läuft das Matching in linearer Zeit zur Eingabegröße, also kann ein bösartiges Muster oder eine bösartige Eingabe dein Programm nicht lahmlegen. Nimm stattdessen Capture Groups und etwas Go-Code oder teil die Prüfung auf zwei Regexes auf.

Was ist der Unterschied zwischen regexp.Compile und regexp.MustCompile?

Compile gibt das kompilierte Muster und einen Fehler zurück. MustCompile löst bei einem ungültigen Muster stattdessen eine Panic aus. Nimm MustCompile für Muster, die in deinem Quelltext stehen, typischerweise in einer Variablen auf Paketebene, damit ein Tippfehler schon beim Start auffällt. Nimm Compile für Muster, die von Nutzern oder aus Konfigurationsdateien kommen.

Wie bekomme ich in Go die Capture Groups eines Regex?

Mit FindStringSubmatch, das einen Slice zurückgibt, in dem Index 0 der gesamte Treffer ist und Index 1, 2, ... die Gruppen sind, oder nil, wenn es keinen Treffer gibt. Für alle Treffer nimm FindAllStringSubmatch(s, -1). Benannte Gruppen (?P<year>\d{4}) findest du mit re.SubexpIndex("year").

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S