What is the bounded-length restriction on lookbehind in Java, and how do you work around an unbounded requirement?
answer
- Lookbehind must have a known MAX length (bounded), not fixed
- Allowed: (?<=ab), (?<=a{1,5}); rejected: (?<=a+), (?<=a{2,})
- Reason: engine must know how far back to step
- Java = bounded (between fixed-only and fully unbounded)
- Workaround: cap the quantifier, capture normally, or check in code
- Lookahead has NO length limit
basics
~20 sJava's lookbehind must match text of a limited, known maximum length. So (?<=ab) is fine and bounded quantifiers like (?<=a{1,5}) work, but truly unbounded ones like (?<=a+) or (?<=a*) are not allowed because the engine must know how far back to look.
solid answer
~50 sIn Java, a lookbehind (?<=X) or (?<!X) must be **obvious-bounded-length**: the engine has to know a finite maximum number of characters the sub-pattern can consume so it can step back that far and try to match forward. Fixed-length (?<=abc) and bounded quantifiers (?<=a{2,4}, ?<=ab?) are accepted. Unbounded quantifiers - +, *, {n,} with no upper bound - are rejected; older JDKs throw PatternSyntaxException, and behavior around alternations of differing lengths has varied across versions. Java does allow alternation and some variable length within a bound, which is more lenient than some engines (.NET allows fully unbounded lookbehind; many PCRE flavors require fixed length). Work-arounds: cap the quantifier to a realistic max ((?<=a{1,100})), restructure so the variable part is consumed normally and captured rather than looked behind, reverse-and-match, or do a normal match plus a programmatic check. Lookahead has no such length restriction.
code
java · 16 linesimport java.util.regex.*;
// Rejected: unbounded lookbehind -> PatternSyntaxException
try {
Pattern.compile("(?<=a+)b");
} catch (PatternSyntaxException e) {
System.out.println("rejected: " + e.getDescription());
}
// Allowed: bounded (capped) quantifier
Matcher ok = Pattern.compile("(?<=a{1,3})b").matcher("aaab");
System.out.println(ok.find()); // true
// Work-around for a variable prefix: capture instead of look behind
String out = " token=secret".replaceAll("(token=)\\w+", "$1REDACTED");
System.out.println(out); // token=REDACTEDgo deeper
Aware that lookbehind cannot be unlimited length; can recognize a rejected pattern.
Knows fixed and capped quantifiers are allowed but + / * are not, and can apply a capped quantifier work-around.
Explains the engine reason (step-back distance), positions Java among fixed/bounded/unbounded engines, and picks the right work-around.
Guides the team away from clever unbounded-lookbehind hacks toward capture/code-side checks and documents engine-portability constraints.
## Why lookbehind is special A **lookahead** is easy for the engine: it is already at the cursor and simply tries to match the sub-pattern *forward* from there. A **lookbehind** must do the opposite - confirm that some pattern matches the text **ending exactly at the cursor**. The naive way to do that is to step the cursor back by N characters and try to match forward, landing exactly on the cursor. To do that the engine must know **how many characters N to step back**. If the looked-behind pattern can be any length (like `a+`, which could be 1 or 1000 `a`s), there is no single N to try, and a fully general backward search would be expensive. For this reason Java requires the lookbehind sub-pattern to be **bounded in length** - it must have a known finite maximum width. ## What is allowed vs rejected **Allowed (bounded):** - Fixed length: `(?<=foo)`, `(?<=\d\d)` - Optional / bounded quantifiers: `(?<=ab?)`, `(?<=a{2,5})`, `(?<=\d{1,3})` - Alternation where each branch is itself bounded: `(?<=cat|dog|fish)` (Java computes the max width across branches) **Rejected (unbounded):** - `(?<=a+)`, `(?<=a*)` - no upper bound - `(?<=a{2,})` - lower bound but no upper bound - `(?<=.*foo)` - `.*` is unbounded Attempting to compile an unbounded lookbehind historically throws `java.util.regex.PatternSyntaxException` with a message like "Look-behind group does not have an obvious maximum length". (Exact diagnostics and the degree of leniency have changed across JDK versions; do not rely on a specific version accepting a borderline pattern.) ## Where Java sits among engines - **Many flavors (older PCRE, Python's built-in `re`):** lookbehind must be **fixed** length - even `(?<=ab?)` is rejected. - **Java:** **bounded** (variable but capped) length - `(?<=a{1,5})` is fine. - **.NET, and Python's `regex` module:** **arbitrary** (unbounded) lookbehind - `(?<=a+)` works. So Java is in the middle: more flexible than fixed-only, less than fully unbounded. ## Work-arounds for an unbounded need 1. **Cap the quantifier.** If you really wanted `(?<=\s+)`, ask what a realistic maximum is and write `(?<=\s{1,200})`. Pragmatic and usually correct. 2. **Consume normally + capture.** Instead of looking behind for the variable part, match it as a normal group and use `group(n)`/replacement back-references. E.g. to act on the part after `key=`, match `(key=)(value)` and rebuild with `$1` rather than `(?<=key=)`. 3. **Match-then-check in code.** Do a simpler regex match and verify the preceding context with plain Java (`matcher.start()`, substring checks). Often clearer than a clever lookbehind. 4. **Reverse the input and use lookahead.** Reverse the string and the pattern; an unbounded lookbehind becomes an unbounded lookahead, which has no length limit. Rarely worth the complexity. 5. **Use a third-party engine** (e.g. the `com.google.re2j` or a PCRE binding) only if the requirement is genuinely unavoidable - usually it isn't. ## Practical guidance Lookahead has **no** length restriction - so when you have a choice, expressing the constraint as a lookahead avoids the problem entirely. Reach for capped quantifiers or a normal capture before anything exotic; an unbounded lookbehind is almost always a sign the logic belongs partly in code.
- Why does lookahead not have a length restriction while lookbehind does?Lookahead matches forward from the current cursor - the engine just runs the sub-pattern in place. Lookbehind must locate text ending at the cursor, so it needs a bounded number of start positions to try; an unbounded width has no fixed step-back distance.
- How do other engines compare on lookbehind length?Python's built-in re and older PCRE require fixed length; Java allows bounded (capped) variable length; .NET and Python's regex module allow fully arbitrary lookbehind.
- Give a clean way to replace text after 'name=' without a variable-length lookbehind.Match a capturing group: Pattern "(name=)\\w+" and replaceAll("$1REDACTED"), keeping the literal prefix via the back-reference instead of looking behind for it.
saying these in an interview costs you the question
- Claiming Java lookbehind must be strictly FIXED length (it allows bounded variable, e.g. a{1,5})
- Saying lookbehind has no restriction at all (confusing it with .NET)
- Trying (?<=.*foo) and expecting it to compile
- Assuming lookahead has the same length restriction