This appendix contains the answers to the exercises that appear throughout the book.
This exercise is easy since we already gave you the program. Your job was to make it work:
print "Hello, world! ";
If you have v5.10 or later, you can try say
:
use v5.10; say "Hello, world!";
If you want to try it from the command line without creating a
file, you can specify your program on the command line with the -e
switch:
$ perl -e 'print "Hello, World
"'
There’s another switch, -l
,
that automatically adds the newline for you:
$ perl -le 'print "Hello, World"'
The quoting in Windows in command.exe (or cmd.exe) needs the double quotes on the outside, so you switch them:
C:> perl -le "print 'Hello, World'"
You can save yourself some headaches with quoting by using the generalized quotes inside the shell quoting:
C:> perl -le "print q(Hello, World)"
With v5.10 and later, you can use the -E
switch to enable new features. That
allows you to use say
:
$ perl -E 'say q(Hello, World)'
We didn’t expect you to try this on the command line, because we didn’t tell you about that yet. Still, it’s another way to do it. See perlrun for a complete listing of command-line switches and features.
The perldoc command should
come with your perl, so you should
be able to run it directly. If you can’t find perldoc, you may have to install another
package on your system. Ubuntu, for instance, puts it in the perl-doc
package.
This program is easy too, as long as you got the previous exercise to work:
@lines = `perldoc -u -f atan2`; foreach (@lines) { s/w<(.+?)>/U$1/g; print; }
Here’s one way to do it:
#!/usr/bin/perl use warnings; $pi = 3.141592654; $circ = 2 * $pi * 12.5; print "The circumference of a circle of radius 12.5 is $circ. ";
As you see, we started this program with a typical #!
line; your path to Perl may vary. We also
turned on warnings.
The first real line of code sets the value of $pi
to our value of π. There are several
reasons a good programmer will prefer to use a constant value like
this: it takes time to type 3.141592654
into your program if you ever
need it more than once. It may be a mathematical bug if you
accidentally used 3.141592654
in
one place and 3.14159
in another.
There’s only one line to check on to make sure you didn’t accidentally
type 3.141952654
and send your
space probe to the wrong planet.
Modern Perls allow you to use fancier characters as variable names. We could have used the π character as the name if we told Perl that the source code included Unicode characters (see Appendix C):
#!/usr/bin/perl use utf8; use warnings; $π = 3.141592654; $circ = 2 * $π * 12.5; print "The circumference of a circle of radius 12.5 is $circ. ";
Next we calculate the circumference, storing it in $circ
, and we print it out in a nice
message. The message ends with a newline character, because every line
of a good program’s output should end with a newline. Without it, you
might end up with output looking something like this, depending on
your shell’s prompt:
The circumference of a circle of radius 12.5 is 78.53981635.bash-2.01$
Since the circumference isn’t really 78.53981635.bash-2.01$
, this should probably
be construed as a bug. So, use
at the end of each line of output.
Here’s one way to do it:
#!/usr/bin/perl use warnings; $pi = 3.141592654; print "What is the radius? "; chomp($radius = <STDIN>); $circ = 2 * $pi * $radius; print "The circumference of a circle of radius $radius is $circ. ";
This is just like the last one, except now we ask the user for
the radius, and then we use $radius
in every place where we previously used the hardcoded value 12.5
. If we had written the first program
with more foresight, in fact, we would have a variable named $radius
in that one as well. Note that if we
hadn’t used chomp
, the mathematical
formula would still have worked because a string like "12.5
"
is converted to the number 12.5
without any problem. But when we print
out the message, it would look like this:
The circumference of a circle of radius 12.5 is 78.53981635.
Notice that the newline character is still in $radius
, even though we’ve used that
variable as a number. Since we had a space between $radius
and the word is
in the print
statement, there’s a space at the
beginning of the second line of output. The moral of the story is:
chomp
your input unless you have a reason not to.
Here’s one way to do it:
#!/usr/bin/perl use warnings; $pi = 3.141592654; print "What is the radius? "; chomp($radius = <STDIN>); $circ = 2 * $pi * $radius; if ($radius < 0) { $circ = 0; } print "The circumference of a circle of radius $radius is $circ. ";
Here we added the check for a bogus radius. Even if the given radius was impossible, the returned circumference would at least be nonnegative. You could have changed the given radius to be zero, and then calculated the circumference too; there’s more than one way to do it. In fact, that’s the Perl motto: There Is More Than One Way To Do It. And that’s why each exercise answer starts with “Here’s one way to do it…”
Here’s one way to do it:
print "Enter first number: "; chomp($one = <STDIN>); print "Enter second number: "; chomp($two = <STDIN>); $result = $one * $two; print "The result is $result. ";
Notice that we’ve left off the #!
line for this answer. In fact, from here
on, we’ll assume that you know it’s there, so you don’t need to read
it each time.
Perhaps those are poor choices for variable names. In a large
program, a maintenance programmer might think that $two
should have the value of 2
. In this short program, it probably
doesn’t matter, but in a large one we could have called them something
more descriptive, with names like $first_response
.
In this program, it wouldn’t make any difference if we forgot to
chomp
the two variables $one
and $two
, since we never use them as strings
once they’ve been set. But if next week our maintenance programmer
edits the program to print a message like: The result of multiplying $one by $two is
$result.
, those pesky newlines will come back to haunt us.
Once again, chomp
unless you have a
reason not to chomp
—like in the
next exercise.
Here’s one way to do it:
print "Enter a string: "; $str = <STDIN>; print "Enter a number of times: "; chomp($num = <STDIN>); $result = $str x $num; print "The result is: $result";
This program is almost the same as the last one, in a sense.
We’re “multiplying” a string by a number of times, so we’ve kept the
structure of the previous exercise. In this case, though, we didn’t
want to chomp
the first input
item—the string—because the exercise asked for the strings to appear
on separate lines. So, if the user entered fred
and a newline for the string, and
3
for the number, we’d get a
newline after each fred
, just as we
wanted.
In the print
statement at the
end, we put the newline before $result
because we wanted to have the first
fred
printed on a line of its own.
That is, we didn’t want output like this, with only two of the three
fred
s aligned in a column:
The result is: fred fred fred
At the same time, we didn’t need to put another newline at the
end of the print
output because
$result
should already end with a
newline.
In most cases, Perl won’t mind where you put spaces in your
program; you can put in spaces or leave them out. But it’s important
not to accidentally spell the wrong thing! If the x
runs up against the preceding variable
name $str
, Perl will see $strx
, which won’t work.
Here’s one way to do it:
print "Enter some lines, then press Ctrl-D: "; @lines = <STDIN>; @reverse_lines = reverse @lines; print @reverse_lines;
…or, even more simply:
print "Enter some lines, then press Ctrl-D: "; print reverse <STDIN>;
Most Perl programmers would prefer the second one, as long as they don’t need to keep the list of lines around for later use.
Here’s one way to do it:
@names = qw/ fred betty barney dino wilma pebbles bamm-bamm /; print "Enter some numbers from 1 to 7, one per line, then press Ctrl-D: "; chomp(@numbers = <STDIN>); foreach (@numbers) { print "$names[ $_ - 1 ] "; }
We have to subtract one from the index number so that the user
can count from 1 to 7, even though the array is indexed from 0 to 6.
Another way to accomplish this would be to have a dummy item in the
@names
array, like this:
@names = qw/ dummy_item fred betty barney dino wilma pebbles bamm-bamm /;
Give yourself extra credit if you checked to make sure that the user’s choice of index was in fact in the range 1 to 7.
Here’s one way to do it if you want the output all on one line:
chomp(@lines = <STDIN>); @sorted = sort @lines; print "@sorted ";
…or, to get the output on separate lines:
print sort <STDIN>;
Here’s one way to do it:
sub total { my $sum; # private variable foreach (@_) { $sum += $_; } $sum; }
This subroutine uses $sum
to
keep a running total. At the start of the subroutine, $sum
is undef
, since it’s a new variable. Then the
foreach
loop steps through the
parameter list (from @_
), using
$_
as the control variable. (Note:
once again, there’s no automatic connection between @_
, the parameter array, and $_
, the default variable for the foreach
loop.)
The first time through the foreach
loop, the first number (in $_
) is added to $sum
. Of course, $sum
is undef
, since nothing has been stored in
there. But since we’re using it as a number, which Perl sees because
of the numeric operator +=
, Perl
acts as if it’s already initialized to 0
. Perl thus adds the first parameter to
0
and puts the total back in
$sum
.
The next time through the loop, the next parameter is added to
$sum
, which is no longer undef
. The sum is placed back in $sum
, and on through the rest of the
parameters. Finally, the last line returns $sum
to the caller.
There’s a potential bug in this subroutine, depending on how
you think of things. Suppose this subroutine was called with an
empty parameter list (as we considered with the rewritten subroutine
&max
in the chapter text). In
that case, $sum
would be undef
, and that would be the return value.
But in this subroutine, it would probably be “more correct” to return
0
as the sum of the empty list,
rather than undef
. (Of course, if
you wish to distinguish the sum of an empty list from the sum of, say,
(3, –5, 2)
, returning undef
would be the right thing to
do.)
If you don’t want a possibly undefined return value, though,
it’s easy to remedy. Simply initialize $sum
to zero rather than using the default of undef
:
my $sum = 0;
Now the subroutine will always return a number, even if the parameter list was empty.
Here’s one way to do it:
# Remember to include &total from previous exercise! print "The numbers from 1 to 1000 add up to ", total(1..1000), ". ";
Note that we can’t call the subroutine from inside the
double-quoted string, so the subroutine call is another separate item
being passed to print
. The total
should be 500500
, a nice round
number. And it shouldn’t take any noticeable time at all to run this
program; passing a parameter list of 1,000 values is an everyday task
for Perl.
Here’s one way to do it:
sub average { if (@_ == 0) { return } my $count = @_; my $sum = total(@_); # from earlier exercise $sum/$count; } sub above_average { my $average = average(@_); my @list; foreach my $element (@_) { if ($element > $average) { push @list, $element; } } @list; }
In average
, we return without
giving an explicit return value if the parameter list is empty. That
gives the caller undef
as the way
to report that there’s no average for an empty list. If the list
wasn’t empty, using &total
makes it simple to calculate the average. We didn’t even need to use
temporary variables for $sum
and
$count
, but doing so makes the code
easier to read.
The second sub, above_average
, simply builds up and returns
a list of the desired items. (Why is the control variable named
$element
, instead of using Perl’s
favorite default, $_
?) Note that
this second sub uses a different technique for dealing with an empty
parameter list.
To remember the last person that greet
spoke to, use a state
variable. It starts out as undef
, which is how we figure out Fred
is the first person it greets. At the
end of the subroutine, we store the current $name
in $last_name
so we remember what it is next
time:
use v5.10; greet( 'Fred' ); greet( 'Barney' ); sub greet { state $last_person; my $name = shift; print "Hi $name! "; if( defined $last_person ) { print "$last_person is also here! "; } else { print "You are the first one here! "; } $last_person = $name; }
This answer is similar to that for the preceding exercise, but
this time we store all the names we have seen. Instead of using a
scalar variable, we declare @names
as a state variable and push each name onto it:
use v5.10; greet( 'Fred' ); greet( 'Barney' ); greet( 'Wilma' ); greet( 'Betty' ); sub greet { state @names; my $name = shift; print "Hi $name! "; if( @names ) { print "I've seen: @names "; } else { print "You are the first one here! "; } push @names, $name; }
Here’s one way to do it:
print reverse <>;
Well, that’s pretty simple! But it works because print
is looking for a list of strings to
print, which it gets by calling reverse
in a list context. And reverse
is looking for a list of strings to reverse, which it gets
by using the diamond operator in a list context. So, the diamond returns
a list of all the lines from all the files of the user’s choice. That
list of lines is just what cat would print out.
Now reverse
reverses the list of
lines, and print
prints them
out.
Here’s one way to do it:
print "Enter some lines, then press Ctrl-D: "; # or Ctrl-Z chomp(my @lines = <STDIN>); print "1234567890" x 7, "12345 "; # ruler line to column 75 foreach (@lines) { printf "%20s ", $_; }
Here we start by reading in and chomping all of the lines of
text. Then we print the ruler line. Since that’s a debugging aid, we’d
generally comment out that line when the program is done. We could
have typed "1234567890"
again and
again, or even used copy-and-paste to make a ruler line as long as we
needed, but we chose to do it this way because it’s kind of
cool.
Now the foreach
loop
iterates over the list of lines, printing each one with the %20s
conversion. If you chose to do so, you
could have created a format to print the list all at once, without the
loop:
my $format = "%20s " x @lines; printf $format, @lines;
It’s a common mistake to get 19-character columns. That happens
when you say to yourself, “Hey, why do we chomp
the input if we’re only going to add
the newlines back on later?” So you leave out the chomp
and use a format of "%20s"
(without a newline). And now,
mysteriously, the output is off by one space. So, what went
wrong?
The problem happens when Perl tries to count the spaces needed
to make the right number of columns. If the user enters hello
and a newline, Perl sees
six characters, not five, since newline is a
character. So it prints 14 spaces and a six-character string, sure that
it gives the 20 characters you asked for in "%20s"
. Oops.
Of course, Perl isn’t looking at the contents of the string to determine the width; it merely checks the raw number of characters. A newline (or another special character, such as a tab or a null character) will throw things off.
Here’s one way to do it:
print "What column width would you like? "; chomp(my $width = <STDIN>); print "Enter some lines, then press Ctrl-D: "; # or Ctrl-Z chomp(my @lines = <STDIN>); print "1234567890" x (($width+9)/10), " "; # ruler line as needed foreach (@lines) { printf "%${width}s ", $_; }
Instead of interpolating the width into the format string, we could have used this:
foreach (@lines) { printf "%*s ", $width, $_; }
This is much like the previous one, but we ask for a column width first. We ask for that first because we can’t ask for more input after the end-of-file indicator, at least on some systems. Of course, in the real world you’ll generally have a better end-of-input indicator when getting input from the user, as we’ll see in later exercise answers.
Another change from the previous exercise’s answer is the ruler
line. We used some math to cook up a ruler line that’s at least as
long as we need, as suggested as an extra-credit part of the exercise.
Proving that our math is correct is an additional challenge. (Hint:
consider possible widths of 50
and
51
, and remember that the right
side operand to x
is truncated, not
rounded.)
To generate the format this time, we used the expression
"%${width}s
"
, which interpolates
$width
. The curly braces are
required to “insulate” the name from the following s
; without the curly braces, we’d be
interpolating $widths
, the wrong
variable. If you forgot how to use curly braces to do this, though,
you could have written an expression like '%'
. $width . "s
"
to get the same format string.
The value of $width
brings up
another case where chomp
is vital.
If you don’t chomp
the width, the
resulting format string would resemble "%30
s
"
. That’s not useful.
People who have seen printf
before
may have thought of another solution. Because printf
comes to us from C, which doesn’t
have string interpolation, we can use the same trick that C
programmers use. If an asterisk (*
)
appears in place of a numeric field width in a conversion, a value
from the list of parameters will be used:
printf "%*s ", $width, $_;
Here’s one way to do it:
my %last_name = qw{ fred flintstone barney rubble wilma flintstone }; print "Please enter a first name: "; chomp(my $name = <STDIN>); print "That's $name $last_name{$name}. ";
In this one, we used a qw//
list (with curly braces as the delimiter) to initialize the hash.
That’s fine for this simple data set, and it’s easy to maintain
because each data item is a simple given name and simple family name,
with nothing tricky. But if your data might contain spaces—for
example, if robert
de niro
or mary kay
place
were to visit Bedrock—this simple method wouldn’t work
so well.
You might have chosen to assign each key-value pair separately, something like this:
my %last_name; $last_name{"fred"} = "flintstone"; $last_name{"barney"} = "rubble"; $last_name{"wilma"} = "flintstone";
Note that (if you chose to declare the hash with my
, perhaps because use
strict
was in effect) you must declare the
hash before assigning any elements. You can’t use my
on only part of a variable, like
this:
my $last_name{"fred"} = "flintstone"; # Oops!
The my
operator works only
with entire variables, never with just one
element of an array or hash. Speaking of lexical variables, you may
have noticed that the lexical variable $name
is being declared inside the
chomp
function call; it is fairly
common to declare each my
variable
as you need it, like this.
This is another case where chomp
is vital. If someone enters the
five-character string "fred
"
and
we fail to chomp
it, we’ll be
looking for "fred
"
as an element
of the hash—and it’s not there. Of course, chomp
alone won’t make this bulletproof; if
someone enters "fred
"
(with a
trailing space), with what we’ve seen so far, we don’t have a way to
tell that they meant fred
.
If you added a check for whether the given key exists
in the hash so that you’ll give the
user an explanatory message when they misspell a name, give yourself
extra points for that.
Here’s one way to do it:
my(@words, %count, $word); # (optionally) declare our variables chomp(@words = <STDIN>); foreach $word (@words) { $count{$word} += 1; # or $count{$word} = $count{$word} + 1; } foreach $word (keys %count) { # or sort keys %count print "$word was seen $count{$word} times. "; }
In this one, we declared all of the variables at the top. People
who come to Perl from a background in languages like Pascal (where
variables are always declared “at the top”) may find that way more
familiar than declaring variables as they are needed. Of course, we’re
declaring these because we’re pretending that use strict
may
be in effect; by default, Perl won’t require such declarations.
Next we use the line-input operator, <STDIN>
, in a list context to read all
of the input lines into @words
, and
then we chomp
those all at once. So
@words
is our list of words from
the input (if the words were all on separate lines, as they should
have been, of course).
Now the first foreach
loop
goes through all the words. That loop contains the most important
statement of the entire program, the statement that says to add one to
$count{$word}
and put the result
back in $count{$word}
. Although
you could write it either the short way (with the +=
operator) or the long way, the short way
is just a little bit more efficient, since Perl has to look up
$word
in the hash just once. For
each word in the first foreach
loop, we add one to $count{$word}
.
So, if the first word is fred
, we
add one to $count{"fred"}
. Of
course, since this is the first time we’ve seen $count{"fred"}
, it’s undef
. But since we’re treating it as a
number (with the numeric +=
operator, or with +
if you wrote it
the long way), Perl converts undef
to 0
for us automatically. The
total is 1
, which is then stored
back in $count{"fred"}
.
The next time through that foreach
loop, let’s say the word is barney
. So, we add one to $count{"barney"}
, bumping it up from
undef
to 1
as well.
Now let’s say the next word is fred
again. When we add one to $count{"fred"}
, which is already 1
, we get 2
. This goes back in $count{"fred"}
, meaning that we’ve now seen
fred
twice.
When we finish the first foreach
loop, then, we’ve counted how many
times each word has appeared. The hash has a key for each (unique)
word from the input, and the corresponding value is the number of
times that word appeared.
So now, the second foreach
loop goes through the keys of the hash, which are the unique words
from the input. In this loop, we’ll see each
different word once. For each one, it says
something like “fred was seen 3
times.
”
If you want the extra credit on this problem, you could put
sort
before keys
to print out the keys in order. If
there will be more than a dozen items in an output list, it’s
generally a good idea for them to be sorted so that a human being who
is trying to debug the program will fairly quickly be able to find the
item they want.
Here’s one way to do it:
my $longest = 0; foreach my $key ( keys %ENV ) { my $key_length = length( $key ); $longest = $key_length if $key_length > $longest; } foreach my $key ( sort keys %ENV ) { printf "%-${longest}s %s ", $key, $ENV{$key}; }
In the first foreach
loop, we
go through all of the keys and use length
to get their lengths. If the length
we just measured is greater than the one we stored in $longest
, we put the longer value in
$longest
.
Once we’ve gone through all of the keys, we use printf
to print the keys and values in two
columns. We use the same trick we used in Exercise 3 from Chapter 5 by interpolating $longest
into the template string.
Here’s one way to do it:
while (<>) { if (/fred/) { print; } }
This is pretty simple. The more important part of this exercise
is trying it out on the sample strings. It doesn’t match Fred
, showing that regular expressions are
case-sensitive. (We’ll see how to change that later.) It does match
frederick
and Alfred
, since both of those strings contain
the four-letter string fred
.
(Matching whole words only, so that frederick
and Alfred
wouldn’t match, is another feature
we’ll see later.)
Here’s one way to do it: change the pattern used in the first
exercise’s answer to /[fF]red/
. You could also have
tried /(f|F)red/
or /fred|Fred/
, but the character class is more
efficient.
Here’s one way to do it: change the pattern used in the first
exercise’s answer to /./
. The backslash is needed
because the dot is a metacharacter, or you could use a character
class: /[.]/
.
Here’s one way to do it: change the pattern used in the first
exercise’s answer to /[A-Z][a-z]+/
.
Here’s one way to do it: change the pattern used in the first
exercise’s answer to /(S)1/
. The S
character class matches the nonwhitespace
character, and the parentheses allow you to use the back reference
1
to match the same character
immediately following it.
Here’s one way to do it:
while (<>) { if (/wilma/) { if (/fred/) { print; } } }
This tests /fred/
only after
we find /wilma/
matches, but
fred
could appear before or after
wilma
in the line; each test is
independent of the other.
If you wanted to avoid the extra nested if
test, you might have written something
like this:
while (<>) { if (/wilma.*fred|fred.*wilma/) { print; } }
This works because you’ll either have wilma
before fred
or fred
before wilma
. If we had written just /wilma.*fred/
, that wouldn’t have matched a
line like fred and wilma
flintstone
, even though that line mentions both of
them.
Folks who know about the logical-and operator, which we showed
in Chapter 10, could do both tests /fred/
and /wilma/
in the same if
conditional. That’s more efficient, more
scalable, and an all-around better way than the ones given here. But
we haven’t seen logical-and yet:
while (<>) { if (/wilma/ && /fred/) { print; } }
The low-precedence short-circuit version works too:
while (<>) { if (/wilma/ and /fred/) { print; } }
We made this an extra-credit exercise because many folks have a
mental block here. We showed you an “or” operation (with the vertical
bar, |
), but we never showed you an
“and” operation. That’s because there isn’t one in regular
expressions. Mastering
Perl revisits this example by using a regular
expression lookahead, something even a bit too advanced for
Intermediate
Perl.
There’s one easy way to do it, and we showed it back in the
chapter body. But if your output isn’t saying before<match>after
as it should,
you’ve chosen a hard way to do it.
Here’s one way to do it:
/a/
(Of course, that’s a pattern for use inside the pattern test
program!) If your pattern mistakenly matches barney
, you probably needed the
word-boundary anchor.
Here’s one way to do it:
#!/usr/bin/perl while (<STDIN>) { chomp; if (/(w*a)/) { print "Matched: |$`<$&>$'| "; print "$1 contains '$1' "; # The new output line } else { print "No match: |$_| "; } }
This is the same test program (with a new pattern), except that
the one marked line has been added to print out $1
.
The pattern uses a pair of
word-boundary anchors inside the parentheses, although the pattern
works the same way when they are placed outside. That’s because
anchors correspond to a place in the string but not to any characters
in the string: anchors have “zero width.”
Admittedly, the first
anchor isn’t really needed, due to details about greediness that we
won’t go into here. But it may help a tiny bit with efficiency, and it
certainly helps with clarity—and in the end, that one wins out.
This exercise answer is the same as the previous exercise answer, but with a slightly different regular expression:
#!/usr/bin/perl use v5.10; while (<STDIN>) { chomp; if (/(?<word>w*a)/) { print "Matched: |$`<$&>$'| "; print "'word' contains '$+{word}' "; # The new output line } else { print "No match: |$_| "; } }
Here’s one way to do it:
m! (w+a) # $1: a word ending in a (.{0,5}) # $2: up to five characters following !xs # /x and /s modifiers
(Don’t forget to add code to display $2
, now that you have two memory variables.
If you change the pattern to have just one again, you can simply
comment out the extra line.) If your pattern doesn’t match just plain
wilma
anymore, perhaps you require
zero or more characters instead of one or more. You may have omitted
the /s
modifier, since there
shouldn’t be newlines in the data. (Of course, if there are newlines
in the data, the /s
modifier could
make for different output.)
Here’s one way to do it:
while (<>) { chomp; if (/sz/) { print "$_# "; } }
We used the pound sign (#
) as
the marker character.
Here’s one way to do it:
/($what){3}/
Once $what
has been
interpolated, this gives a pattern resembling /(fred|
barney){3}/
. Without the
parentheses, the pattern would be something like /fred|barney{3}/
, which is the same as
/fred|barneyyy/
. So the
parentheses are required.
Here’s one way to do it:
my $in = $ARGV[0]; if (! defined $in) { die "Usage: $0 filename"; } my $out = $in; $out =~ s/(.w+)?$/.out/; if (! open $in_fh, '<', $in ) { die "Can't open '$in': $!"; } if (! open $out_fh, '>', $out ) { die "Can't write '$out': $!"; } while (<$in_fh>) { s/Fred/Larry/gi; print $out_fh $_; }
This program begins by naming its one and only command-line
parameter, and complaining if it didn’t get it. Then it copies that to
$out
and does a substitution to
change the file extension, if any, to .out
. (It would be sufficient, though, to
merely append .out
to the
filename.)
Once the filehandles $in_fh
and $out_fh
are opened, the real
program can begin. If you didn’t use both options /g
and /i
, take off half a point, since
every fred
and
Fred
should be changed.
Here’s one way to do it:
while (<$in_fh>) { chomp; s/Fred/ /gi; # Replace all FREDs s/Wilma/Fred/gi; # Replace all WILMAs s/ /Wilma/g; # Replace the placeholder print $out_fh "$_ "; }
This replaces the loop from the previous program, of course. To
do this kind of a swap, we need to have some “placeholder” string that
doesn’t otherwise appear in the data. By using chomp
(and adding the newline back for the
output), we ensure that a newline (
) can be the placeholder. (You could
choose some other unlikely string as the placeholder. Another good
choice would be the NUL character,