--- name: regex substitution --- # Substitution `s///` is the search-and-replace operator. Its general form is ``` $target =~ s/pattern/replacement/modifiers; ``` It finds the first position where *pattern* matches in *target*, replaces that match with *replacement*, and returns the number of substitutions made. With `/g`, it does that for every non-overlapping match. ```perl my $x = "Time to feed the cat!"; $x =~ s/cat/hacker/; # $x is now "Time to feed the hacker!" ``` If the pattern does not match, the target is unchanged and `s///` returns a false value. ## Operating on $_ Like `m//`, `s///` defaults to `$_` when no `=~` is given: ```perl for (@lines) { s/\s+$//; # trim trailing whitespace from each line } ``` ## Counting substitutions The return value of `s///` in scalar context is the number of replacements: ```perl my $count = ($text =~ s/\btodo\b/done/gi); print "cleared $count todos\n"; ``` In boolean context, zero means no match. ## The replacement string The replacement is a double-quoted string, so variables and escape sequences interpolate: ```perl my $suffix = ".bak"; $file =~ s/$/$suffix/; # append .bak to the name $x =~ s/\t/ /g; # each tab becomes four spaces ``` Inside the replacement, `$1`, `$2`, `$&`, `$+` and friends refer to what the *current* match just captured: ```perl my $y = "'quoted words'"; $y =~ s/^'(.*)'$/$1/; # strip surrounding single quotes # $y is now "quoted words" ``` Named captures work the same way through `$+{name}`: ```perl "2026-04-23" =~ s/(?\d{4})-(?\d{2})-(?\d{2})/$+{d}\/$+{m}\/$+{y}/; # result: "23/04/2026" ``` ## Escaping in the replacement The replacement is interpreted as a double-quoted string, so the double-quote rules apply to *it*, independently of the pattern. Metacharacters like `*`, `+`, `?`, `$` retain their double-quoted meanings — only `$` and `@` trigger interpolation: ```perl $x =~ s/(\w+)/[$1]/g; # wrap every word in brackets $x =~ s/\$/USD/g; # replace literal '$' with 'USD' ``` Backslash escape sequences in the replacement follow the double-quote rules — `\t`, `\n`, `\x{…}` all work. The special case-modification escapes `\l`, `\u`, `\L`, `\U`, `\E`, `\Q` apply in the replacement too: ```perl $x =~ s/(\w+)/\u$1/g; # capitalise the first letter of each word $x =~ s/(\w+)/\U$1\E/g; # uppercase whole word ``` ## /g — replace all Without `/g`, `s///` replaces only the first match. With `/g`, it replaces every non-overlapping match from left to right: ```perl my $x = "I batted 4 for 4"; $x =~ s/4/four/; # "I batted four for 4" $x = "I batted 4 for 4"; $x =~ s/4/four/g; # "I batted four for four" ``` `/g` in substitution is unrelated to `/g` in matching. Here it just means "do this again and again until the pattern stops matching." ## /e — evaluate the replacement Under `/e`, the replacement is Perl code, not a string. The code runs for every match and its return value replaces the match. ```perl my $x = "numbers: 1 2 3 4"; $x =~ s/(\d+)/$1 * 2/ge; # "numbers: 2 4 6 8" ``` The match variables `$1`, `$2`, … are visible inside the code block. Practical uses: ```perl # Uppercase the first letter of every sentence. $text =~ s/(^|\.\s+)(\w)/$1\U$2/g; # Hex-encode non-ASCII bytes. $bytes =~ s/([\x80-\xff])/sprintf "\\x%02x", ord $1/ge; # Apply a lookup table. my %subst = (red => 0xff0000, green => 0x00ff00, blue => 0x0000ff); $css =~ s/\b(red|green|blue)\b/sprintf "#%06x", $subst{$1}/ge; ``` `/ee` evaluates *twice* — first as code, then the result is itself evaluated as Perl. Rarely the right tool; when it is, use it sparingly and with care: ```perl my $var = "greeting"; our $greeting = "hello"; "greeting" =~ s/(\w+)/\$$1/ee; # first: "\$greeting"; second: "hello" ``` ## /r — non-destructive substitution `/r` returns the new string instead of modifying the target. The target is untouched: ```perl my $orig = "I like dogs"; my $new = $orig =~ s/dogs/cats/r; # $orig is still "I like dogs" # $new is "I like cats" ``` If the pattern does not match, `/r` returns the original string unchanged: ```perl my $x = "I like dogs"; my $y = $x =~ s/elephants/cougars/r; # $y eq "I like dogs" ``` The big win is chaining: ```perl my $slug = $title =~ s/[^\w\s-]//gr # drop punctuation =~ s/\s+/-/gr # collapse spaces to dash =~ s/--+/-/gr; # collapse runs of dashes ``` Each arrow returns a new string, which the next `s///r` receives. ## Alternate delimiters Both halves of `s///` can use matched brackets; the delimiters do not have to be the same: ```perl s{pattern}{replacement}g; sg; s[pattern][replacement]g; s(pattern)[replacement]g; # legal, but avoid mixing for clarity ``` And any printable punctuation works in a single-character form: ```perl s!/usr/local/!/opt/!g; s#pattern#replacement#; ``` Useful when the pattern or replacement contains `/`. ## Single-quoted substitution `s'pattern'replacement'` treats both halves as single-quoted — no variable interpolation, no backslash escapes apart from `\'` and `\\`: ```perl s'@users'@admins'g; # literal '@users' becomes literal '@admins' ``` Rarely needed; mention it for completeness. ## Combining with /m, /s, /x Substitution modifiers compose with match modifiers, so you can get the full toolbox: ```perl # Trim each line. $x =~ s/^\s+|\s+$//mg; # Collapse blank lines. $x =~ s/\n{2,}/\n\n/g; # Replace C-style comments across newlines. $src =~ s{/\*.*?\*/}{}gs; # /s lets . cross newlines ``` With `/x` the pattern is readable, with `/s` it spans newlines, with `/g` it repeats. ## A real example Turn a block of English into a slug — lowercase, hyphens instead of spaces, drop non-letters, collapse and trim: ```perl sub slug { my ($s) = @_; return lc($s) =~ s/[^\w\s-]//gr # remove punctuation =~ s/\s+/-/gr # spaces to hyphens =~ s/-+/-/gr # collapse hyphens =~ s/^-|-$//gr; # trim leading/trailing hyphens } print slug("Hello, World! -- Regex Tutorial"); # prints: hello-world-regex-tutorial ``` ## Things that look like substitution but aren't `tr///` (also written `y///`) does character-by-character transliteration, not regexp substitution. It accepts two lists of characters; each occurrence of the nth character in the first list becomes the nth character in the second list. No regexp features apply: ```perl my $x = "Hello"; $x =~ tr/A-Za-z/a-zA-Z/; # swap case (my $hex = $bytes) =~ tr/\x00-\xff//d; # delete all bytes — pointless ``` `tr///` is faster than `s///` for character-level work because it is not a regexp engine. See [`tr`](../../p5/core/perlfunc/tr) for full semantics. ## See also - [`s`](../../p5/core/perlfunc/s) — substitution operator reference. - [`tr`](../../p5/core/perlfunc/tr) — character transliteration; not substitution but often confused for it. - [`quotemeta`](../../p5/core/perlfunc/quotemeta) — escape a string for safe interpolation into a pattern. - The [modifiers](modifiers) chapter — `/g`, `/e`, `/r`, `/ee`, and how they interact.