LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1717 min read

Pattern Matching & Substitution

Learn how to transform text in Perl with the s/// substitution operator, the /g and /i modifiers, and the tr/// transliteration operator.

Introduction

Matching text is useful, but Perl's real editing power comes from being able to transform text in place. The s/// operator finds a pattern and replaces it with something else, and tr/// translates individual characters. Together, these two operators handle the vast majority of real-world text-cleanup tasks.

What You Will Learn
  • How s/// finds and replaces text.
  • How the /g and /i modifiers change substitution behavior.
  • How to reuse captured groups inside a replacement.
  • How tr/// translates or counts individual characters.

The s/// Substitution Operator

The s/// operator has three parts separated by slashes: the pattern to search for, the replacement text, and optional modifiers after the final slash. By default it replaces only the first match it finds.

use strict;
use warnings;
my $message = "I love Java programming!";
$message =~ s/Java/Perl/;
print "$message\n";
Program Output

Click Run to see what this code prints.

Notice that s/// modifies the variable in place — it uses the same =~ binding operator, but instead of just testing the string, it rewrites it.

The /g Modifier

By default s/// stops after the first replacement. Adding the /g (global) modifier makes it replace every matching occurrence in the string, not just the first one.

use strict;
use warnings;
my $csv = "apple,banana,cherry,date";
(my $tabbed = $csv) =~ s/,/\t/g;
print "$tabbed\n";
Program Output

Click Run to see what this code prints.

Copying Before Substituting

(my $tabbed = $csv) =~ s/,/\t/g; is a common idiom: it copies $csv into a new variable first, then substitutes on the copy, leaving the original $csv untouched.

The /i Modifier

The /i modifier makes the match case-insensitive, so the pattern matches regardless of upper or lower case in the target string. It can be combined with /g in any order, such as /gi or /ig.

use strict;
use warnings;
my $text = "Perl is great. PERL is powerful. perl is fun.";
(my $censored = $text) =~ s/perl/Rust/gi;
print "$censored\n";
Program Output

Click Run to see what this code prints.

Substitution With Captures

Just like in a plain match, you can use parentheses in the pattern half of s/// to capture pieces of the matched text, then reference them in the replacement half using $1, $2, and so on. This lets you rearrange text, not just remove or replace it outright.

use strict;
use warnings;
my $name = "Doe, John";
$name =~ s/(\w+), (\w+)/$2 $1/;
print "$name\n";
Program Output

Click Run to see what this code prints.

This pattern captures the last name into $1 and the first name into $2, then the replacement swaps their order — a very common data-cleanup task in real-world Perl scripts.

The tr/// Operator

tr/// (also spelled y///) works differently from s///: instead of matching patterns, it translates individual characters one-for-one, based on two lists of characters you provide. It is much faster than a regex for simple character-level transformations, and it can also count characters without changing anything, if you pass no replacement list.

use strict;
use warnings;
my $dna = "GATTACA";
(my $complement = $dna) =~ tr/ACGT/TGCA/;
print "Complement: $complement\n";
my $sentence = "Hello, World!";
my $vowel_count = ($sentence =~ tr/aeiouAEIOU//);
print "Vowel count: $vowel_count\n";
Program Output

Click Run to see what this code prints.

tr/// Returns a Count

tr/// always returns the number of characters it matched (or replaced), which is why it doubles as a handy character-counting tool even when the replacement list is left empty.

Common Mistakes

Avoid These Mistakes
  • Forgetting /g and being surprised that only the first match got replaced.
  • Assuming tr/// uses regular expressions — it does not; it only maps individual characters, not patterns.
  • Using s/// without capturing when a swap or rearrangement is needed, then wondering where the original text went.
  • Modifying a string with s/// or tr/// when you actually wanted to keep the original unchanged (remember the (my $copy = $original) =~ ... idiom).
  • Mixing up the order of the two character lists in tr///, which silently produces the wrong mapping.

Best Practices

  • Use /g whenever you intend to replace every occurrence, not just the first.
  • Reach for tr/// instead of s/// for simple single-character replacements or counting — it is faster and clearer.
  • Use named or numbered captures in s/// to rearrange or reformat text rather than deleting and retyping it manually.
  • Combine /i with /g carefully, and test against mixed-case real data before shipping a substitution.
  • Prefer the copy-then-substitute idiom when the original string still needs to be preserved elsewhere in the program.

Frequently Asked Questions

s/// performs full pattern-based find-and-replace using regular expressions. tr/// only translates individual characters based on two matching-position lists, with no regex support.

Yes, with the /r modifier: my $new = $original =~ s/foo/bar/r; leaves $original untouched and returns the modified copy instead.

s/// returns the number of substitutions it performed, so you can capture that directly: my $count = ($text =~ s/foo/bar/g);

Yes, y/// is simply an older, alternative spelling of the exact same operator as tr///.

Key Takeaways

  • s/pattern/replacement/ finds and replaces text, modifying the string in place.
  • The /g modifier replaces every match; /i makes the match case-insensitive.
  • Captured groups in the pattern can be reused in the replacement as $1, $2, and so on.
  • tr/// translates individual characters and can also count matching characters.
  • The /r modifier returns a modified copy instead of changing the original string.

Summary

With matching and substitution covered, you now have Perl's full text-processing toolkit at your disposal. Next, you will shift focus to organizing code itself, starting with subroutines — how to package logic into reusable, named blocks.

Next Lesson →

Subroutines