skip to content

How do capturing-group back-references like $1 work in a Java regex replacement string, and how do you insert a literal $ or backslash?

level: middleimportance: should knowfreq 58%

answer

  1. $1..$n = group captures; $0 = whole match; ${name} = named group
  2. Groups numbered by opening paren, starting at 1
  3. $ and \ are special in the REPLACEMENT string
  4. \$ and \\ insert literal $ and backslash
  5. Matcher.quoteReplacement = escape dynamic replacement text

basics

~20 s

Inside 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 s

When 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

for a junior

Recognizes that $1 inserts captured text and that you sometimes have to escape $.

for a middle

Explains group numbering, $0/${name}, and uses Matcher.quoteReplacement for dynamic replacements.

for a senior

Distinguishes the two failure modes (IllegalArgumentException vs IndexOutOfBoundsException) and pairs quoteReplacement with quote as the mirror tools for each side.

for a principal

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)

context