MarkofPerl has asked for the wisdom of the Perl Monks concerning the following question:

I have tried every suggestion I could find on how to pull (aka Slurp) the text of a file into a perl program and all of them that say they should work weren't. I am on a Mac Mini with Lion (10.7), and the perl version is 5.12. Here are some that I've tried:

#!/usr/local/env Perl5.12 ### filename:Hello.pl use strict; use File::Slurp; my $string; open (my $QBF, "<QBF.txt"); $string = read_file($QBF); print "\n"; print $string + "\n"; # doesn't print anything. (or prints "0") print "\n";

## -----------------

use File::Slurp; open my $fh, "<", "QBF.txt" or die $!; local $/; # enable localized slurp mode my $content = <$fh>; close $fh; print $content; # doesn't print anything.

At least last night is wasn't working. I had started with a while loop which worked fine by itself:

print"\n"; open (my $QBF, "QBF.txt"); while (<$QBF>) { print; }

after that I was trying to add the other methods for "slurping", into that same program, and it wasn't until I segregated them into two separate files that the second version worked.

use File::Slurp; open my $fh, "<", "QBF.txt" or die $!; local $/; # enable localized slurp mode my $content = <$fh>; close $fh; print $content; # prints now.

So what I'm wondering is if i put these two pieces of code together, is the read pointer already at the end of the file, and that's why they won't work in succession? It seemed like I was only getting one output even though I had two different routines for extracting (one just prints, the other saves the file contents to a local scalar for processing, and then prints.) Also I'd like to know more about what "$/" is and how it works Thanks.

Replies are listed 'Best First'.
Re: Contents of a file into a scalar
by davido (Cardinal) on Aug 09, 2013 at 23:01 UTC

    The concatenation operator in Perl is dot ("."), not plus ("+"). You're evaluating the slurped-in string in numeric context, and presumably it has a value that numerifies to zero. That should handle your first example.

    Your second example works for me. And it seems that you are stating that your third and fourth examples do function as you expect. So it's really just the first that's a problem. Use the dot operator for concatenation.

    Also in your first example you're not checking for open failure with an "or die $!" clause. That would be a good practice.


    Dave

      So what you're suggesting is, that in the first example, everything is more or less correct except the line that says $string = read_line($QBF);, and should be replaced with $string .= read_line($QBF);?

      I'm aware of the "or die!" stuff, I'm just debugging right now, and I know the file is there.

      And the second poster (toolic) says I shouldn't bother with the '+' in-between data types when printing? (sorry, I come from the land of C++.)

        Incorrect. I'm not talking about $string = read_line($QBF);. That line is probably working fine, assuming the file opened correctly (which you can't be sure of, because you didn't use "or die $!;". Debugging time is the most important time to have error reporting in place.

        What I am talking about is this line: "print $string + "\n"; # doesn't print anything. (or prints "0")"

        That line should not be adding $string to \n. It should be either concatenating with the 'dot' operator, or passing a list, with the comma operator. In other words, either of these would work:

        print $string . "\n"; print $string, "\n";

        You are using the addition operator. The addition operator applies numeric context to your "$string". Perl will convert that to the numeric value of "0", unless the string happens to begin with a number, or consists entirely of a number. Then it does the same thing to "\n". You said that "print $string + "\n";" prints "0". I'm saying the reason it prints zero is because both $string and \n, in the numeric context applied by "+" morph themselves into values of zero. Zero plus zero is zero, and that's what gets printed.

        If you used either of the print statements I suggested, no such numification would occur, and assuming open worked ok, you would see the results you're after.

        In C++ you would often overload '+' to deal gracefully with various data types. Unfortunately, + is also overloaded in C++ to concatenate std::string objects. Perl reserves "+" for math, and '.' for concatenation.

        So in C++ you might say:

        cout << my_string + "\n"; // Concatenation with newline. cout << my_string << std::endl; // Stream insertion with standard lin +e ending. cout << my_string << "\n"; // Stream insertion with newline.

        In Perl, similar semantics are invoked with:

        print $string . "\n"; # Concatenation with newline. print $string, $/; # List of items to print, with standard +line ending. print $string, "\n"; # List of items to print, with newline. # Or more conveniently... print "$string\n"; # Interpolation of a variable and newlin +e.

        Dave

        And the second poster (toolic) says I shouldn't bother with the '+' in-between data types when printing?
        Correct. In Perl, + is used to add numbers (Additive Operators).
Re: Contents of a file into a scalar
by Kenosis (Priest) on Aug 10, 2013 at 02:48 UTC

    File::Slurp handles all of the file i/o, so you don't need to open the file first. Try the following:

    use strict; use warnings; use File::Slurp; my $string = read_file 'QBF.txt'; print "\n"; print $string . "\n"; print "\n";
Re: Contents of a file into a scalar
by tobyink (Canon) on Aug 10, 2013 at 07:13 UTC

    As Kenosis said you're doing too much work. File::Slurp does it all for you:

    use File::Slurp 'read_file'; my $string = read_file($filename);

    In the absence of File::Slurp, the best way is:

    my $string = do { open my $fh, '<', $filename; local $/; <$fh>; };

    Or even:

    my $string = do { local (@ARGV, $/) = $filename; <> };

    ... though that last one can boggle some readers' minds.

    You can read about the magic $/ variable in perlvar, but the short story is: $/ is the string that Perl considers to be the line ending when it's reading a file line-by-line; when $/ is undef, Perl will consider the whole file to be a single line, so <$fh> (which normally reads a single line from $fh) will read the whole file.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Contents of a file into a scalar
by toolic (Bishop) on Aug 09, 2013 at 23:04 UTC
Re: Contents of a file into a scalar
by AnomalousMonk (Archbishop) on Aug 10, 2013 at 17:48 UTC
    MarkofPerl: I'm aware of the "or die!" stuff, I'm just debugging right now ...
    davido: Debugging time is the most important time to have error reporting in place.

    Yea and amen, brothers and sisters in Perl! Ignore these wise words at the peril of your sanity.

    Update:

    ... if i put these two pieces of code together, is the read pointer already at the end of the file, and that's why they won't work in succession?
    I don't know how File::Slurp works (maybe read the docs), but if an ordinary file handle is used to read a file all the way to the end, the file is 'exhausted' and any further built-in read operations will return nothing. See seek to re-position the logical file pointer to the beginning of the file (or anywhere else in the file).