Find and Replace with Regex Capture Groups: A Practical Guide for Writers
Put parentheses around the part of the text you want to keep, then reinsert it in the replacement field with $1, $2 and so on (or \1, \2, depending on your editor's regex flavor). One move, and you can reorder, reuse, and reformat text across a whole document in a single pass.
Quick answer
A capture group is a pair of parentheses in the find pattern. Whatever the group matches is remembered, and the replacement can put it back anywhere, in any order, as many times as you like.
The classic example comes from the MDN documentation for `String.replace()`:
Find: (\w+)\s(\w+)
Replace: $2, $1
Maria Cruz -> Cruz, MariaTwo rules keep you safe: preview every match before you hit Replace All, and run bulk edits on a copy of the file.
Step-by-step
1. Wrap what you want to keep in parentheses
Groups are numbered by the position of their opening parenthesis, counting from the left, starting at 1. Nested groups follow the same rule, as the MDN guide to groups and backreferences shows: in ((a)b), the outer group is 1 and the inner group is 2.
Text outside parentheses is still matched, and still replaced. It just isn't remembered. That's useful: you can match a prefix, drop it, and keep only what comes after.
2. Reinsert the groups in the replacement
In the replacement field, refer to a group by number. You can reorder groups, repeat them, or leave some out. Regular-Expressions.info notes that one group can be reused many times and that groups can be rearranged.
The syntax depends on the engine:
| Style | Looks like | Used by | |---|---|---| | Dollar | $1, $2 | JavaScript, .NET, Java, ICU, PCRE2 | | Backslash | \1, \2 | Python, Ruby, RE2, Tcl |
If one doesn't work in your editor, try the other, then check its documentation.
3. Reformat dates
Say your notes use 03/14/2026 and you want 2026-03-14, so files and entries sort correctly. (More on why in our piece on YYYY-MM-DD date-first file names.)
Find: (\d{2})/(\d{2})/(\d{4})
Replace: $3-$1-$2
03/14/2026 -> 2026-03-14Group 1 is the month, group 2 the day, group 3 the year. The replacement simply lists them in a new order with hyphens between.
4. Swap link forms
Converting wikilinks to standard Markdown links is a good job for a capture group. The trade-offs are covered in wikilinks vs Markdown links; the mechanical part looks like this:
Find: \[\[([^\]|]+)\]\]
Replace: [$1]($1.md)
[[Meeting notes]] -> [Meeting notes](Meeting notes.md)The pattern escapes the literal brackets, then captures everything up to the next closing bracket or pipe. Notice that $1 is used twice. File names with spaces usually need extra handling afterward, so inspect a few results before trusting the rest.
5. Convert a heading style
If you wrote section titles as bold lines and want real Markdown headings:
Find: ^\*\*(.+)\*\*$
Replace: ## $1
**Quarterly goals** -> ## Quarterly goalsThe ^ and $ anchors tie the match to a whole line, so bold text in the middle of a sentence stays alone. You may need to switch on a "multiline" or "match line by line" option for those anchors to work per line.
6. Name your groups when patterns get long
Past three groups, numbers get hard to track. Named groups give each capture a label. In JavaScript the syntax is (?<name>...) in the pattern and $<name> in the replacement:
Find: (?<last>\w+), (?<first>\w+)
Replace: $<first> $<last>
Cruz, Maria -> Maria CruzOther flavors spell the reference differently. Regular-Expressions.info lists ${name} for .NET, PCRE2, Java 7 and ICU, and \g<name> for Python. Some tools, including PHP, R, RE2 and Google Sheets, don't support named references in replacements at all, so there you count parentheses instead.
Common problems and fixes
You see a literal `$1` in the output. Either the pattern has no first group (no parentheses), or your editor uses the other syntax. In JavaScript, a reference to a group that doesn't exist is left in as literal text, per MDN.
The number runs into the next digit. If you want group 1 followed by a literal 0, $10 can be read as group 10. Some flavors accept ${1}0 to separate them; Regular-Expressions.info lists .NET, Perl, PCRE2 and Boost. Check your editor's documentation, or restructure the pattern.
An optional group is missing. A group that exists but didn't take part in the match is replaced with an empty string. Often that's what you want. It can also leave stray punctuation behind.
The match swallows too much. .+ is greedy and runs to the last possible spot on the line. Use .+? for the shortest match, or a negated class like [^\]]+.
Groups shifted after you edited the pattern. Adding a capturing group renumbers everything to its right. If you need parentheses only for grouping or alternation, use a non-capturing group, (?:...), which stores nothing and takes no number.
Anchors behave oddly on some files. Invisible line-ending differences can stop $ from matching where you expect. See CRLF vs LF line endings.
Before any Replace All, step through the first few matches, or at least scan the match count. If the number looks wrong, the pattern is wrong. Copy the file first so an undo isn't your only safety net.
Doing this with Carets
Regex replacement is a plain-text operation, which is why it pairs well with a plain-text editor. Your notes in Carets are ordinary Markdown and text files, so a bulk edit never touches a proprietary format, and a copy of a file before a risky change is a one-step habit. Carets is a native editor for iPhone, iPad, and Mac, so the same files are within reach wherever you edit them.
Two features help with this kind of work. Syntax highlighting makes patterns and code snippets easier to read when you keep your favorite find-and-replace recipes in a snippet file (our piece on how syntax highlighting works explains why regexes sit at the heart of it). And files, projects, and tags let you keep recipes for dates, links, and headings next to the notes they apply to.
One caveat: check which find-and-replace options your editor offers, and which regex flavor it speaks, before relying on a pattern from this page. The ideas carry over; the exact replacement syntax may not.
Get Carets for iPhone, iPad, and Mac on the App Store.
Frequently Asked Questions
Why does my replacement insert a literal $1 instead of the captured text?
Either the pattern has no group 1 (no parentheses), or the editor uses a different syntax such as \1. In JavaScript, a reference to a group that doesn't exist stays as literal text. Check the group count and your editor's replacement syntax.
What is the difference between $1 and \1?
They do the same job in different engines. $1 is used by JavaScript, .NET, Java, and PCRE2-based tools; \1 by Python, Ruby, RE2, and others. Some editors accept only one.
How do I use parentheses for grouping without capturing?
Use a non-capturing group, (?:...). It groups for quantifiers or alternation but doesn't take a number, so later groups keep their numbers.
Can I undo a bad Replace All?
Usually with the editor's undo, but don't rely on it for a whole folder. Work on a copy or a version-controlled file, and preview the matches first.