How do capturing-group back-references like $1 work in a Java regex replacement string, and how do you insert a literal $ or backslash?
answer
- $1..$n = group captures; $0 = whole match; ${name} = named group
- Groups numbered by opening paren, starting at 1
- $ and \ are special in the REPLACEMENT string
- \$ and \\ insert literal $ and backslash
- Matcher.quoteReplacement = escape dynamic replacement text
basics
~20 sInside the replacement string, $1, $2, ... insert the text captured by the matching parentheses (groups) of the pattern. To put a literal $ or \ in the output instead, escape it as $ / \, or wrap the whole replacement in Matcher.quoteReplacement.
solid answer
~40 sWhen you call replaceAll/replaceFirst (or Matcher.replaceAll), the replacement string is not copied verbatim. A dollar sign followed by digits, like $1, is a back-reference that gets substituted with whatever the corresponding capturing group `( ... )` matched in the input; $0 is the whole match. Named groups use ${name}. A backslash escapes the next character. This means a literal $ or \ in your output must be written as \$ or \\ in source, otherwise you get a wrong substitution or an IllegalArgumentException (e.g. a $ not followed by a valid group). The safe, general fix is Matcher.quoteReplacement(s), which escapes every special character so the string is inserted exactly as-is, which is essential when the replacement comes from untrusted or arbitrary data.
go deeper
Recognizes that $1 inserts captured text and that you sometimes have to escape $.
Explains group numbering, $0/${name}, and uses Matcher.quoteReplacement for dynamic replacements.
Distinguishes the two failure modes (IllegalArgumentException vs IndexOutOfBoundsException) and pairs quoteReplacement with quote as the mirror tools for each side.
Treats unescaped dynamic replacement as an injection/correctness hazard and codifies quoteReplacement use in shared utilities and reviews.
## Groups and back-references A **capturing group** is a parenthesized part of a regex, e.g. in `(\w+)@(\w+)` there are two groups: group 1 is the part before `@`, group 2 the part after. Groups are numbered left-to-right by their opening `(`, starting at 1; **group 0** is the entire match. In the **replacement string** of `replaceAll`, `replaceFirst`, or `Matcher.replaceAll/replaceFirst`, you can refer back to what a group captured: - `$1`, `$2`, … → the text captured by group 1, 2, … - `$0` → the whole matched substring - `${name}` → a **named** group `(?<name>...)` Example — swap `first last` into `last, first`: ```java "John Smith".replaceAll("(\\w+) (\\w+)", "$2, $1"); // "Smith, John" ``` Here `(\\w+)` in Java source is the regex `(\w+)` (`\\` is one backslash). `$1` and `$2` pull the captured words. ## Why `$` and `\` are dangerous in replacements Because `$` and `\` are *processed*, a replacement that contains them as ordinary characters misbehaves: - `replaceAll("x", "100$")` — the `$` is not followed by a digit/`{`, so it throws `IllegalArgumentException`. - `replaceAll("x", "$5")` when there is no group 5 → `IndexOutOfBoundsException`. - A replacement read from user input containing `$0` would silently re-insert the whole match instead of the literal text. ## Inserting them literally — two ways 1. **Manual escaping:** write `\\$` for a literal `$` and `\\\\` for a literal `\` (in Java source the doubling is because `\` is also a Java string escape). The two-character regex-replacement tokens are `\$` and `\\`. 2. **`Matcher.quoteReplacement(String s)`** — returns a version of `s` with every `\` and `$` escaped, so the result is inserted **exactly** as written. This is the robust choice whenever the replacement text is dynamic: ```java String repl = userText; // may contain $ or \ str.replaceAll(pattern, Matcher.quoteReplacement(repl)); ``` ## Mental model - **Pattern side** → use `Pattern.quote` to neutralize specials (covered separately). - **Replacement side** → use `Matcher.quoteReplacement` to neutralize `$`/`\`. They are mirror tools for the two halves of a substitution.
- What happens if your replacement string is the literal currency text "$100" passed straight to replaceAll?It throws IllegalArgumentException because $ must be followed by a group reference. Use "\\$100" or Matcher.quoteReplacement("$100").
- How do you reference a named group in the replacement?With ${name}, matching a (?<name>...) group in the pattern. For example replaceAll("(?<y>\\d{4})", "${y}!").
saying these in an interview costs you the question
- Believing the replacement string is copied verbatim
- Inserting user input directly as the replacement (breaks on $ or \)
- Confusing Pattern.quote (pattern side) with Matcher.quoteReplacement (replacement side)
- Thinking group numbering starts at 0 for the first parentheses (0 is the whole match)