0

Have been playing with the following script but still couldn't understand the meaning behind the two different "kinds" of filehandle forms. Any insight will be hugely appreciated.

#! usr/bin/perl
use warnings;
use strict;
open (FH, "example.txt") or die $!;

while (<FH>) {
    my @line = split (/\t/, $_); {
        print "@line","\n";
    }
}

The output is as expected: @line array contains elements from line 1,2,3 ... from example.txt. As I was told that open (FH, example.txt) is not as good as open (my $fh, '<', 'example.txt'), I changed it but then confusion arose.

From what I found, $fh is scalar and contains ALL info in example.txt. When I assigned an array to $fh, the array stored each line in example.txt as a component in the array. However, when I tried to further split the component into "more components", I got the error/warning message "use of uninitialized value". Below is the actual script that shows the error/warning message.

open (my $fh, '<', 'example.txt') or die $!;
foreach ($fh) {
    my @line = <$fh>;
    my $count = 0;
    for $count (0..$#line) {
        my @line2 = split /\t/, $line[$count];
        print "@line2";
        print "$line2[0]";
        }
    }

print "@line2" shows the expected output but print "$line2[0]" invokes the error/warning message. I thought if @line2 is a true array, $line2[0] should be okay. But why "uninitialized value" ??

Any help will be appreciated. Thank you very much.

Added - the following is the "actual" script (I re-ran it and the warning was there)

#! usr/bin/perl
use warnings;
use strict;
open (my $fh, '<', 'example.txt') or die $!;

foreach ($fh) {
    my @line = <$fh>;
    print "$line[1]";
    my $count = 0;
    for my $count (0..$#line) {
    my @line2 = split /\t/, $line[$count];
    print "@line2";
    #my $line2_count = $#line2;
    #print $line2_count;
    print "$line2[3]";
    }
    }

The warning is still use of uninitialized value $line2[3] in string at filename.pl line 15, <$fh> line3.

B Chen
  • 923
  • 2
  • 12
  • 21
  • 1
    Are you sure? I was able to run the same code without any problem. – Mir Jun 06 '15 at 15:36
  • Second example with foreach does not make any sense, and there is your problem, not three args open. while(<$fh>) .. reads from filehandle scalar in samme way as globtype does. – mpapec Jun 06 '15 at 15:45
  • 2
    @Сухой27, The `foreach ($fh) { }` makes no sense, but only because it doesn't do anything. It causes no harm except to readability. – ikegami Jun 06 '15 at 15:46
  • 4
    @B Chen, The only way you could get that warning there is if `$line[$count]` was an empty string, but that's impossible. It contains at least a newline. The code you posted did not produce the error you claimed it did. Please post the code that actually produces the warning, along with the necessary data. – ikegami Jun 06 '15 at 15:50

2 Answers2

1

In your second example, you are reading the filehandle in a list context, which I think is the root of your problem.

my $line = <$fh>;

Reads one line from the filehandle.

my @lines = <$fh>;

Reads all the file. Your former example, thanks to the

while (<FH>) {

Is effectively doing the first case.

But in the second example, you are doing the second thing.

Sobrique
  • 52,974
  • 7
  • 60
  • 101
-2

AFAIK, you should always use

while (<FH>) {
   # use $_ to access the content
}

or better

while(my $single_line = <FH>) {
   # use $single_line to access the content
}

because while reads line by line where for first loads all in memory and iterates it after.

Even the returns undef on EOF or error, the check for undef is added by the interpreter when not explicitly done.

So with while you can load multi gigabyte log files without any issue and without wasting RAM where you can't with for loops that require arrays to be iterated.

At least this is how I remember it from a Perl book that I read some years ago.

My Too
  • 39
  • 3
  • There is no difference between `while (my $line = )` and `while(defined(my $line = ))`. – Sinan Ünür Jun 07 '15 at 13:04
  • It is always better doing it correct. I has no impact any more but in older perl versions: `#!/usr/bin/env perl use strict; use warnings; open(FH, "test.txt") or die "ERROR: $!"; while () { print "----" . $_ . "+++"; } ` becomes: `$ perl -MO=Deparse ~/Desktop/testMe.pl use warnings; use strict; die "ERROR: $!" unless open FH, 'test.txt'; while (defined($_ = )) { print '----' . $_ . '+++'; } testMe.pl syntax OK ` And the lacy `while (my $line = )` will be translated to `while(defined(my $line = ))` as well. – My Too Jun 07 '15 at 23:44
  • see http://stackoverflow.com/questions/9621221/usage-of-defined-with-filehandle-and-while-loop so why not just doing it correctly as the old perl books tell you anyway? – My Too Jun 07 '15 at 23:51
  • At least 2010 this was still true and 2010 is not too long ago. See "Effective Perl Programming: Ways to Write Better, More Idiomatic Perl, Second Edition By: Joseph N. Hall; Joshua A. McAdams; brian d foy Effective Perl Programming" Chapter 1. The Basics of Perl; Item 7. Know which values are false and test them accordingly – My Too Jun 08 '15 at 00:12
  • I repeat, there is absolutely no reason whatsoever to write `while(defined(my $single_line = )) {` when you can write `while (my $single_line = ) {`. I do not understand what argument you are trying to make with your previous comments. If you want to enhance your answer, edit it to incorporate new information, instead of cramming code and output into comments. – Sinan Ünür Jun 08 '15 at 01:41
  • The point is just that your `while (my $line = )` is translated to `while(defined(my $line = ))` as well because this would be the correct behavior. You can verify this by executing your code with `perl -MO=Deparse`. Anyway, just writing it does not hurt and does not rely on the goodwill of the interpreter. Yes it is an edge case only relevant if the last line of a file has content "" or 0 and if a perl version old (5.004 it seems). But except of that your example is shorter, it does exactly what mine does and mine is more stable. – My Too Jun 08 '15 at 04:49
  • 1
    As the guy who wrote the book that you reference, I say listen to Sinan as he tells you something more specific than the general rule. Your version isn't more or less stable because it's the same. It's a documented special case that most people who know what they are doing are going to write. – brian d foy Jun 08 '15 at 09:40
  • you are right http://perldoc.perl.org/perlop.html#I%2fO-Operators shows says that all are similar in context of while() and for(;;) but must be checked for defined( ) in other contexts. – My Too Jun 10 '15 at 20:58