s/// and tr/// return counts¶
Level: 101 · you wrote my $clean = $dirty =~ s/.../.../
One line: s/// and tr/// change their target and return how many substitutions or characters they found — so my $new = $old =~ s/a/b/g stores a number, map { s/^\s+// } @lines returns counts and edits the array, and a failed s/// returns the empty string — unless you add /r, which leaves the target alone and returns the new string.
Measured¶
Verified output of s_and_tr_pl.pl — regenerated by tools/run_examples.py, never hand-typed.
--- s/// returns a count ---
returned: 2
target: /opt/local/bin:/opt/bin:/bin
no match: '' (the empty string, not 0)
--- so map { s/// } returns counts ---
map { s/^\s+|\s+$//g } returned '1', '1', ''
and changed the array: 'alpha', 'beta', 'gamma'
map { s/^\s+|\s+$//gr } returned 'alpha', 'beta', 'gamma'
and left the array: ' alpha', 'beta ', 'gamma'
--- s///e runs the replacement as code ---
tea 5.54, cake 14.76
--- tr/// counts characters, and is not a regex ---
tr/s// 4 and the target is still Mississippi
tr/a-zA-Z//cdr HelloWorld
tr/./_/r 1_2_3 a dot is a dot
tr/a-z// on 'zażółć' counts 2 the range is code points 0x61 to 0x7A
tr/$letter// on 'letters' counts 6 the set is $ l e t r, not s
eval "tr/$letter//" on 'letters' counts 1
s/// edits and counts¶
$path =~ s{/usr}{/opt}g changed $path and returned 2, the number of replacements. With no match it returned '' — false, but not 0, so printing it shows nothing.
map with s///¶
map aliases $_ to each element, so s/// inside it edits the array itself, and map collects what s/// returned: 1, 1, ''. Both results are wrong for "give me the trimmed lines" — the list is counts, and the source is gone. /r fixes both: it works on a copy and returns the copy.
s///e¶
With /e the replacement is Perl code, evaluated per match: sprintf '%.2f', $1 * 1.23 turns each price into a price with 23% added.
tr/// is not a regex¶
tr — also spelled y — maps characters to characters. It returns the count of characters it matched, so tr/s// with an empty replacement counts without changing anything. And it is not a pattern:
.is a dot.tr/./_/rreplaces dots, not every character.a-zis a range of code points, 0x61 to 0x7A.ż,ó,łandćare outside it, sozażółćcounts 2.- Variables are not interpolated.
tr/$letter//is the set of characters$,l,e,t,r— it counts 6 inletters, and never looks at$letter.perlopdocuments stringevalas the way to build atrfrom a variable, which the last line shows; with data you did not write, build a character class fors///instead.
What to do instead¶
my $clean = $dirty =~ s/\s+\z//r; # a new string
my @trimmed = map { s/^\s+|\s+\z//gr } @lines;
my $count = ($text =~ tr/\n//); # count characters
(my $copy = $original) =~ s/a/b/g; # the older idiom for a copy, before /r
If you are coming from another language¶
- Python.
stris immutable, sore.subalways returns a new string — Perl's/ris the default there — andre.subnreturns the count alongside it.str.translateistr. - The
trcommand. The encodings library'strandsortwork a byte at a time ↗ — Perl'strcounts code points once the string is decoded; the shell's counts bytes.
See also¶
/gin list and scalar context — whatm//returnschompleaves the\r—chompreturns a count too- perlop,
s///andtr///↗