http://qs1969.pair.com?node_id=11135401

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

When I tried to read nextline, the pointer in the while loop reads two lines at a time. Is there another creative way to deal with the file's next line?

use strict; use warnings; my %store; my $nextline; my $cnt=1; my $filename = "$ARGV[0]"; open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open fi +le '$filename' $!"; while (my $row = <$fh>) { chomp $row; my @tmp = split /\s+/, $row; $nextline = <$fh>; chomp $nextline; my @nexttmp = split /\s+/, $nextline; print "$row\t$cnt\n"; if ($tmp[0] ne $nexttmp[0]) { $cnt++; } print "$nextline\t$cnt\n"; } close $filename;

Replies are listed 'Best First'.
Re: Dealing with nextline of a file
by eyepopslikeamosquito (Archbishop) on Jul 27, 2021 at 11:02 UTC

    Adapting my reply to Reading files n lines a time illustrates another approach:

    use strict; use warnings; my $fname = shift or die "usage: $0 fname\n"; open(my $fh, '<', $fname) or die "error: open '$fname': $!"; my $chunksize = 2; my $chunk = ""; while ( my $line = <$fh> ) { $chunk .= $line; next if $. % $chunksize; print "---chunk---\n$chunk"; $chunk = ""; } close $fh; if (length $chunk) { print "---chunk (leftover)---\n$chunk"; }

    Sample output with two simple test files, one with four lines:

    ---chunk--- one two ---chunk--- three four

    and one with five lines:

    ---chunk--- one two ---chunk--- three four ---chunk (leftover)--- five

    For your use-case you would obviously do something different to my crude printing of chunks above.

    Some Similar PM Nodes

Re: Dealing with nextline of a file
by Corion (Patriarch) on Jul 27, 2021 at 08:48 UTC

    Instead of looking at the next line, consider looking at the last line and the current line:

    my $lastline; while (my $currentline = <$fh>) { chomp $currentline; my @tmp = split /\s+/, $row; ... $lastline = $currentline; }

    Alternatively, you can look at accessing the file as an array, using Tie::File.

    If you're dealing with CSV data, consider using Text::CSV_XS.

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Dealing with nextline of a file
by LanX (Saint) on Jul 27, 2021 at 10:12 UTC
Re: Dealing with nextline of a file
by Marshall (Canon) on Jul 28, 2021 at 05:43 UTC
    You general approach is just fine. However, if you are always reading 2 lines at a time, why not just read them both in the while conditional? If there are an odd number of lines, below code will just throw away that last line that doesn't have a partner following it. The other question is what to do with blank lines at the end? Maybe somebody hit <cr> again a few times? Or mistake in cut-n-paste? I did something simple to allow for an undef resulting from split on "\n". Other implementations are of course possible, I don't know enough about your actual app.

    I try to avoid stuff like $tmp[0] because you need a comment to describe what the heck the 0th field actually means! I used an array slice to move the indexing into the split so that I could assign this first column a scalar name. You should use terminology appropriate for your application. I didn't see the need for chomp, only to add a "\n" back in. The split will remove any \n if there is just one token on the line. Also, I personally wouldn't put multiple statements on the same line. Perl doesn't care, but people do.

    I would do something different if this involved reading chunks of more than 2 lines. But this specific case, I find the below adequate.

    use strict; use warnings; my $cnt=1; while (defined (my $first = <DATA>) and defined (my $second = <DATA>) +) { my $name1 = (split /\s+/, $first)[0] // last; # handle trailing my $name2 = (split /\s+/, $second)[0] // last; # potential "\n" bl +ank lines print "$cnt\t$first"; if ($name1 ne $name2) { $cnt++; } print "$cnt\t$second"; } =prints 1 one 1 one 1 two 1 two 1 three 1 three 1 three 2 four note: same result with or without the trailing blank line =cut __DATA__ one one two two three three three four Odd line here followed by a blank line if you want
    Update: Oh, I usually put the numbering on the left. I know how big that column is going to get. Done the other way around, a long token will screwup your nice printout. You didn't specify what is to happen with an odd number of line - it could be "silent ignore" approach above is not what you want - but I couldn't tell.
Re: Dealing with nextline of a file
by BillKSmith (Monsignor) on Jul 28, 2021 at 14:22 UTC
    Any of the "sliding window" approaches will answer your question. However, I suspect that you have an XY Problem. If you describe what you need to count, we may be able to suggest a better algorithm. My best guess is that you want the number of distinct values for the first field in your data. Can you be sure that like values are always consecutive?
    Bill
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Dealing with nextline of a file
by tybalt89 (Monsignor) on Jul 28, 2021 at 01:17 UTC

    Here's two creative ways - slurp the whole file, then use regex lookahead to get the "next" line, or use tell() and seek() to read ahead then reset the current file position.

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11135401 use warnings; open my $fh, '<', \<<END or die; line one line two line three line four line five END { local $/; local $_ = <$fh>; while( /^(.*)\n(?=(.*))/gm ) { print "$1 -> $2\n"; } } print "\n"; seek $fh, 0, 0; # reset for different example while( <$fh> ) { chomp; my $pos = tell $fh; print"$_ -> ", (<$fh> // "AT LAST LINE\n"); seek $fh, $pos, 0; }

    Outputs:

    line one -> line two line two -> line three line three -> line four line four -> line five line five -> line one -> line two line two -> line three line three -> line four line four -> line five line five -> AT LAST LINE
Re: Dealing with nextline of a file
by fishy (Friar) on Jul 31, 2021 at 20:36 UTC
    Hi jnarayan81,

    you can adapt the following code, seen on the web as "Rolling buffer" (by Toby Inkster or Ingy döt Net, can't remember):
    For $n=2, on each iteration, $cache[0] and $cache[1] will contain a pair of adjacent lines.
    #!/usr/bin/perl ## Go through a stream and prints all but the last n lines where n is +a command line argument: my @cache; my $n = shift @ARGV; while(<>) { push @cache, $_; print shift @cache if @cache > $n; }