Free — no signup required

Lookbehinds: Checking the Past

3 min read

What a Lookbehind Does

A lookbehind is the mirror image of a lookahead. Instead of peeking forward, the engine checks what came before the current position. Like a lookahead, it is zero-width: the checked text is not consumed and does not appear in the match result.

The practical use case is extracting a value that is always preceded by a known label or delimiter, without including that label in your result.

Positive Lookbehind (?<=...)

Syntax: (?<=CONDITION)PATTERN

The engine checks whether CONDITION immediately precedes the current position. If it does, it proceeds to match PATTERN.

Example — extract dollar amounts without the currency symbol:

Pattern:  (?<=\$)\d+(\.\d{2})?
String:   Total: $149.99   → Matches: 149.99
          Discount: $20    → Matches: 20
          Code: 200        → No match (no $ prefix)

Operational example — extract values from structured log fields:

Pattern:  (?<=latency_ms=)\d+
String:   ts=2024-01-15 level=info latency_ms=342 status=200
Matches:  342

This is far more precise than splitting on spaces and hoping the field order never changes.

Negative Lookbehind (?<!...)

Syntax: (?<!CONDITION)PATTERN

The engine checks that CONDITION does not immediately precede the current position.

Example — match "ID:" references, but not "Group ID:":

Pattern:  (?<!Group )ID: \d+
String:   User ID: 123      → Match: ID: 123
          Group ID: 456     → No match
          Session ID: 789   → Match: ID: 789

Operational example — match port numbers not preceded by a colon (i.e., not part of an IPv6 address):

Pattern:  (?<!:)\b\d{4,5}\b
String:   Connect to host:8080    → Matches: 8080
          IPv6: ::1:443           → No match for 443 (preceded by :)

Engine Support and Constraints

Lookbehind support varies significantly across regex engines:

Engine Positive Lookbehind Negative Lookbehind Variable-length Lookbehind
Python (re) ✅ ✅ ❌ (fixed-width only)
Python (regex module) ✅ ✅ ✅
JavaScript (ES2018+) ✅ ✅ ✅
Go (regexp) ❌ ❌ ❌
PCRE / PHP ✅ ✅ ❌ (fixed-width only)
Java ✅ ✅ ✅ (since Java 8)

The most important constraint: in most engines, the lookbehind pattern must be fixed-width. You cannot write (?<=\w+) because \w+ is variable length. You must write (?<=\w{1,10}) or restructure the pattern. The regex module in Python and modern JavaScript are exceptions.

In Go, lookbehinds are entirely absent. The idiomatic Go approach is to use a capturing group and extract group 1: latency_ms=(\d+) then read match[1].

Interview Tip

A common interview scenario: "You need to parse structured log lines in Go and extract field values. How do you handle the lack of lookbehind support?" The correct answer is to use a capturing group — fieldname=(\d+) — and reference the first capture group from the match result. This is functionally equivalent to a lookbehind for extraction purposes. Bonus points for mentioning that Go's regexp package uses RE2 syntax, which deliberately excludes features that cannot be evaluated in linear time, making it immune to ReDoS attacks by design.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free