Albert Kurniawan Kuswandi Albert Kurniawan Kuswandi - 1 month ago 6
Perl Question

How to read two lines from a file in perl

I am wondering why I can't seem to take in 2 lines in Perl
A snippet of my code is

#!/usr/bin/perl -w

open my $fh, "<", "data.txt" or die;

$line1 = <$fh>;
$line2 = <$fh>;

print $line1."\n";
print $line2."\n";

while(1){
@rows = <$fh>;
print "$rows[$e]";
$e++;
print "$rows[$e]";
}


This is the kind of data I have in Data.txt

5000 5000 12497500 0
5000 5000 33258530 0
5000 13 51822 0
5000 13 130147 0
5000 28 75016 0
5000 28 181343 0
5000 5000 4999 4999
5000 5000 12580635 4999


I have looked around for possible solutions, I found some interesting leads here http://www.perlmonks.org/?node_id=620077, particularly these ones

my $fh = open("file");

for =$fh->$line1,$line2 {
# do whatever with the two lines
}


and

for (;;) {
my $line1 = <$fh>;
last if not defined $line1;

my $line2 = <$fh>;
last if not defined $line2;

...
}


Could I also ask what
for =$fh->$line1,$line2
why is there a =$ and why does it not work or how does it work?

Any help is appreciated!

Cheers

UPDATE
Alright, so I might be missing on something, but this is the operation I wanted to do from the lines

while(1){
print '-'x50 . "\n";
my $line1 = <$fh>;

if($line1 =~ /^\s+(\d)\s+(\d)\s+(\d)\s+(\d)\s*$/){
my $A1 = $1;
my $B1 = $2;
my $C1 = $3;
my $D1 = $4;
}
last unless defined $line1;

my $line2 = <$fh>;

if($line2 =~ /^\s+(\d)\s+(\d)\s+(\d)\s+(\d)\s*$/){
my $A2 = $1;
my $B2 = $2;
my $C2 = $3;
my $D2 = $4;
}
last unless defined $line2;

my @type = ("AL", "PL", "RL", "AA", "PA", "RA", "AB", "PB", "RB", "AR", "PR", "RR", "AS", "PS", "RS", "AV", "PV", "RV");


print("For $type[$i]\n");
print("Node : $A1\n");
print("Depth : $B1\n");
print("Nb : $C1\n");
print("Nb+s : $C1+$C2\n");
print("Nk : $A1\n");
print("Nu : $A1/6\n");
print("Average number of comparisons is $C2/(7/6*$A1)\n");
$i++;
}
print '--- END ---'."\n";


This is based on xxFelixxxx's response

Answer

Inside your while loop, you call @rows = <$fh>; which will take all of the lines of your file and put them into @rows. The next time through the while loop, it will attempt to do the same thing...but there are no more lines left, so @rows will be empty. So if you need all of the rows, grab them outside of your while loop. If you just need 2 lines at a time, you can just grab 2 rows inside your loop and check that they are not empty:

#!/usr/bin/env perl

use strict;
use warnings;

open my $fh, "<", "data.txt" or die "Unable to open data.txt for reading : $!";

while(1) {
    print '-'x50 . "\n";
    my $line1 = <$fh>;
    last unless defined $line1;
    print "ODD  LINE $. : $line1";

    my $line2 = <$fh>;
    last unless defined $line2;
    print "EVEN LINE $. : $line2";
}
print '--- SUCCESS ---' . "\n";

Output

--------------------------------------------------
ODD  LINE 1 :   5000    5000    12497500    0
EVEN LINE 2 :   5000    5000    33258530    0
--------------------------------------------------
ODD  LINE 3 :   5000    13    51822    0
EVEN LINE 4 :   5000    13    130147    0
--------------------------------------------------
ODD  LINE 5 :   5000    28    75016    0
EVEN LINE 6 :   5000    28    181343    0
--------------------------------------------------
ODD  LINE 7 :   5000    5000    4999    4999
EVEN LINE 8 :   5000    5000    12580635    4999
--------------------------------------------------
--- SUCCESS ---

Update

In your updated version, you have a scoping issue, in that the construct of

if (...) { 
    my ($foo) = 123;
}
print $foo; # <--- out of scope

means that the rest of your code will not see the variables $A1,$B1,$C1,$D1. The use of regular expressions is not necessary and can be more clearly expressed using a split on whitespace and a grep on digits, for sanity checking. The construct of my @foo = ("abc","def","ghi"); is more perlishly expressed as my @foo = qw( abc def ghi );

Here is an updated version:

#!/usr/bin/env perl

use strict;
use warnings;

open my $fh, "<", "data.txt" or die "Unable to open data.txt for reading : $!";

my @types = qw(AL PL RL AA PA RA AB PB RB AR PR RR AS PS RS AV PV RV);

while(1) {
    print '-'x50 . "\n";
    # TODO: better names than a,b,c,d
    my ($A1,$B1,$C1,$D1) = get_row($fh);
    last unless defined $A1;

    my ($A2,$B2,$C2,$D2) = get_row($fh);
    last unless defined $A2;

    my $type = shift @types; # Pull one off the front
    defined $type
        or die "No more types left!";

    my $Nb_plus_s = $C1 + $C2;
    # sprintf("%0.2f", 123.456789) -> 123.46   # round to 2 decimals
    my $Nu = sprintf("%0.2f", $A1 / 6);
    my $avg_comparisons = sprintf("%0.2f", $C2/(7/6*$A1));

    print "For $type\n";
    print "Node  : $A1\n";
    print "Depth : $B1\n";
    print "Nb    : $C1\n";
    print "Nb+s  : $Nb_plus_s\n";
    print "Nk    : $A1\n";
    print "Nu    : $Nu\n";
    print "Average number of comparisons is $avg_comparisons\n";
}
print '--- SUCCESS ---' . "\n";

sub get_row {
    my ($file_handle) = @_;
    my $line = <$file_handle>;
    return unless defined $line;

    # Split on whitespace, keep only numbers
    my @data = grep {/\d/} split /\s+/, $line;

    # Check that we have 4 columns of data
    (4 == scalar @data)
        or die "Cannot understand line: '$line'";

    return(@data);
}

New Output

--------------------------------------------------
For AL
Node  : 5000
Depth : 5000
Nb    : 12497500
Nb+s  : 45756030
Nk    : 5000
Nu    : 833.33
Average number of comparisons is 5701.46
--------------------------------------------------
For PL
Node  : 5000
Depth : 13
Nb    : 51822
Nb+s  : 181969
Nk    : 5000
Nu    : 833.33
Average number of comparisons is 22.31
--------------------------------------------------
For RL
Node  : 5000
Depth : 28
Nb    : 75016
Nb+s  : 256359
Nk    : 5000
Nu    : 833.33
Average number of comparisons is 31.09
--------------------------------------------------
For AA
Node  : 5000
Depth : 5000
Nb    : 4999
Nb+s  : 12585634
Nk    : 5000
Nu    : 833.33
Average number of comparisons is 2156.68
--------------------------------------------------
--- SUCCESS ---