Getting file contents into a single variable (Larry Wall, Programming Perl) saves enormous time in handling text files.
=pod Embellish as you will (slurpall(), etc. This example only does one file at a time.) When I do this fast or in-line, many times I have accidentally left out the concatenation dot in ( .= ). Depending on your buffer size and file size, it can be a transient error that is a PITA. "Remember the dot". Better, package it. Reading a file a buffer at a time, rather than a line at a time, though, makes slurpping an invaluable tool of the trade (... $/="";). Usage: $html = slurp("index.html"); =cut sub slurp { my $tls = $/; $/=""; open X, $_ or return "No $_\n"; my $slurp .= $_ while (<X>); close X; $/ = $tls; return $slurp; }

Replies are listed 'Best First'.
RE: slurp
by chromatic (Archbishop) on Apr 19, 2000 at 00:32 UTC
    Besides btrott's objections, setting a magic variable (the ever-lovable $/) that way is a recipe for debugging disaster!

    Because the magic variables are relatively global, a change in one forgotten corner of your program somewhere can have far-reaching effects on other code. For example, if I were to write the (habitual) code thusly:

    open (INPUT, $filename) || die "Can't open $filename: $!"; while (<INPUT>) { hack($_); slice($_); dice($_); vacuum_seal($_); } close INPUT || die "Wow, we're in for it now: $!";
    I assumed that the Input Record Separator ($/) was set to its default of "\n", as it almost always is. If I had called your subroutine earlier, it wouldn't be set to that.

    Imagine the pain and suffering if that were in a module.

    The solution is twofold. First, use local. This is one of the few places where it's absolutely necessary. Second (related to the first), place the entire code in a block -- that way, the local copy of $/ will be in effect only for that block, and the normal value will be in effect everywhere else.

      That's a REALLY good point chromatic, but I wanted to emphasize the second half of your "twofold solution" which is to put the code in a block. This is very important, because otherwise your usage of local will still be relatively global.
RE: slurp
by btrott (Parson) on Apr 19, 2000 at 00:15 UTC
    I don't have Programming Perl, but I highly doubt that the book advised using that code to slurp a file into a variable, because it doesn't work. :) (Not that technical books are always correct, but this is rather spectacularly incorrect--not to mention incomplete.)

    The main problem with this (aside from the fact that it's not complete) is that you've set the input record separator ($/) incorrectly. Setting it to "" tells Perl to read in paragraphs at a time, not the whole file.

    To read in the entire file, set $/ to undef. So your sub should look something like this:

    sub slurp { local $/ = undef; local *X; open X, $_[0] or die "Can't open $_[0]: $!"; my $slurp = <X>; close X or die "Can't close $_[0]: $!"; $slurp; }
    Your other problem, which I've fixed, is that you were using $_, not $_[0].

    The code should be used like this:

    my $contents = slurp("/home/foo/bar.txt");
    The sub will die when it can't open or close the file, so you may want to change that; but you should always check for errors, no matter what.
      there you go, but please, not "spectacularly". That's what happens when you're in a hurry. I slapped it off without running the code. Nevermore. 'incomplete' is expected isn't it? But thanks for the comeuppance. I have shunned $/=undef since perl 4.something, because of an inconsistency then, but it works solidly in 5+. Like a stutter, I find it hard to shake. And, of course, in the example slurp contains nothing when while finishes. Vane --Watch the stars