Robert's Perl Tutorial

http://www.sthomas.net/roberts-perl-tutorial.htm


$/ -- Changing what is read into $_

On a different note, what if your input file is doesn't look like this:

Beer
Wine
Pizza
Catfood

which is nicely delimited with a newline each time, but like this:

shorts
t-shirt
blouse

pizza
beer
wine
catfood

Viz
Private Eye
The Independent
Byte

toothpaste
soap
towel

which is delimited by TWO newlines, not one. You don't have to save the above as shop.txt, but if you don't, the examples will be difficult to follow.

Now, if you want each set of items as elements in an array you'll have to do something like this:

$SHOP="shop.txt";
$x=0;

open SHOP or die "Can't open $SHOP for read: $!\n";

while (<SHOP>) {
        if (/^\n/) {            # does line begin with newline ?
                $x++;           # if so, increment $x.  Rest of if statement not executed.
        } else {
                $list[$x].=$_;  # glue $_ on the end of whatever is in $list[$x], using a .
        }               
}

foreach (@list) {
        print "Items are:\n$_\n\n";
}

which works, but there is a much easier way to do it. You knew I was going to say that.

$SHOP="shop.txt";
$/="\n\n";

open SHOP or die "Can't open $SHOP for read: $!\n";

while (<SHOP>) {
        push (@list, $_);
}

foreach (@list) {
        print "Items are:\n$_\n\n";
}

The $/ variable is a special variable (it even looks special). It is the Default Input Record Separator. Remember the operation of the angle brackets being to read a file in up until the next newline ? Time to come clean. What the angle bracket actually do is read up until whatever $/ is set to. It is set to a newline by default.

So if we set it to two newlines, as above, then it reads up until it finds two consecutive newlines, then puts the data into $_ This makes the program a lot shorter and quicker. You can set $/ to just about anything, not just a newline. If you want to hack this list for example:

Tea:Beer:Wine:Pizza:Catfood:Coffee:Chicken:Salmon:Icecream

you could just leave $/ as a newline and slurp it into memory in one go, but imagine the above items are a list of clothes that your girlfriend wants to buy or a list of clothes your boyfriend should have thrown away by now. Either are going to be really big files, and you might not want to read it all into memory in one go. So set $/=":"; and all will be well. There are also read and seek functions, but they aren't covered here. Those are useful for files where you read in a precise number of bytes.

We'll go back to the last example for a moment. It is useful to know how to read just one line (well, up to $/ ) at a time:

$SHOP="shop.txt";
$/="\n\n";

open SHOP or die "Can't open $SHOP for read: $!\n";

$clothes=<SHOP>;        # everything up until the first occurrence of $/ into $clothes

$food=<SHOP>;   # everything from first occurrence of $/ to the second into $food

print "We need...\n",$clothes,"...and\n",$food;

And now we know that, there is a even quicker way to achieve the aim of the original program :

$SHOP="shop.txt";
$/="\n\n";

open SHOP or die "Can't open $SHOP for read: $!\n";

@list=<SHOP>;   # dumps *all* of $SHOP into @list, not just one line.

foreach (@list) {
        print "Items are:\n$_\n\n";
}

and you don't need to grab it all :

@list[0..2]=<SHOP>;

We haven't mentioned list context for a while. Whether the line input operator <> returns a single value or a list depends on the context you use it in. When you supply @xxxxx then this must be a list. If you supply $xxxxx then that's a scalar variable. You can force it into list context by using parens.

The two lines below are provided so you can paste them into the above program. They demonstrate how parens force list context. Remember to replace the foreach with something that prints the variables.

($first, $second) = <SHOP>;
$first,  $second  = <SHOP>;