Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Reading the contents of a file from the bottom up

by Scotmonk (Sexton)
on Nov 24, 2019 at 18:26 UTC ( [id://11109149]=perlquestion: print w/replies, xml ) Need Help??

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

Using a While loop, is it possible to read a file from the last line, and then up, rather than from the first line and down ?

So I have a data file like this:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
17 18 19 20
21 22 23 24

I need to read this file and print it to another file in the format

21 22 23 24 17 18 19 20 13 14 15 16

with the specification of no repeated values, and to the number of values that I decide,

so if I wanted to read 7 values, the output file would contain

21 22 23 24 17 18 19 - read in this order (reading in this order is important).

I am just setting out learning PERL, so please be gentle with me.

  • Comment on Reading the contents of a file from the bottom up

Replies are listed 'Best First'.
Re: Reading the contents of a file from the bottom up
by stevieb (Canon) on Nov 24, 2019 at 19:28 UTC

    There's an API for that... File::ReadBackwards. I've never used it myself, but it appears to do what the documentation states.

    Using a file called test.txt located in the same directory as the script itself which looks like this:

    f e d c b a

    ...and this example script:

    use warnings; use strict; use File::ReadBackwards; my $file = 'test.txt'; my $fh = File::ReadBackwards->new($file) or die "can't read the damned '$file' file!: $!"; until ($fh->eof) { print $fh->readline; }

    ...I get this output:

    a b c d e f

      Actually, no need for an API, sort of. There's PerlIO::reverse which lets you just add a layer when opening a file, then use your good ol' Perl API:

      open my $fh, "<:reverse", "/etc/passwd"; print for <$fh>;

      Like File::ReadBackwards, but unlike the tac command, PerlIO::reverse only does line-by-line reversing though.

Re: Reading the contents of a file from the bottom up
by roboticus (Chancellor) on Nov 24, 2019 at 20:28 UTC

    Scotmonk:

    If the file is small enough that it fits comfortably in memory, you could just read the whole thing and then reverse it, like this:

    # foo.pl - print a file in reverse use strict; use warnings; my $FName = shift // die "Expected a filename!"; open my $FH, '<', $FName or die "Can't open $FName: $!\n"; # Read all the lines my @lines = <$FH>; # Reverse the order @lines = reverse @lines; for my $line (@lines) { print $line; }

    When I run it on itself, I get:

    $ perl foo.pl foo.pl } print $line; for my $line (@lines) { @lines = reverse @lines; # Reverse the order my @lines = <$FH>; # Read all the lines open my $FH, '<', $FName or die "Can't open $FName: $!\n"; my $FName = shift // die "Expected a filename!"; use warnings; use strict; # foo.pl - print a file in reverse

    Sometimes a file is too big to handle that way, though that's increasingly rare with modern computers. But if so, you could always use a program (such as tac) that will reverse the file for you, and then you can process the file in order in your code.

    For one-off jobs, that's the way I'd go. But if you always need to process the files in reverse, I'd use the File::Backwards module that stevieb suggested.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      thankyou
Re: Reading the contents of a file from the bottom up
by kcott (Archbishop) on Nov 25, 2019 at 08:32 UTC

    G'day Scotmonk,

    Here's some basic logic which may achieve what you want.

    #!/usr/bin/env perl use strict; use warnings; my $COUNT = $ARGV[0]; my %seen; my @data; unshift @data, split while <DATA>; @data = grep !$seen{$_}++, @data; print "@data[0 .. $COUNT - 1]\n"; __DATA__ 1 1 2 2 3 3 4 5 6 7 8 5 6 6 7 8 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 17 18 19 20 21 22 23 24

    Some sample runs:

    $ ./pm_11109149_reverse_file_extract_with_conds.pl 24 21 22 23 24 17 18 19 20 13 14 15 16 9 10 11 12 5 6 7 8 1 2 3 4 $ ./pm_11109149_reverse_file_extract_with_conds.pl 12 21 22 23 24 17 18 19 20 13 14 15 16 $ ./pm_11109149_reverse_file_extract_with_conds.pl 7 21 22 23 24 17 18 19

    Notes:

    • This solution is intended for small files (such as the sample input you show). Some of the techniques shown may also be valid for larger files — such as using %seen for duplicate checking — but you probably won't want to read millions of numbers into an array if you only want seven.
    • I've modified your data to include duplicates in various places. For future reference, it's useful for us if you provide sample input with examples of this sort of thing.
    • I've just used the DATA filehandle; you'll need to add your own I/O handling.
    • I haven't included any exception handling; you'll need to do this. Areas to look at would be I/O errors, $COUNT not being an integer, and so on.
    "I am just setting out learning PERL, ..."

    That's fine: none of us left the womb knowing any Perl.

    [Aside: PERL is wrong: the language is Perl and the executable is perl (which may have an extension on some systems, e.g. perl.exe on MSWin).]

    If there's any code that I've used that you don't understand, try the Online Perl Documentation in the first instance — if still in doubt, ask another question here.

    — Ken

      Thankyou very much for your help with this
Re: Reading the contents of a file from the bottom up
by NetWallah (Canon) on Nov 25, 2019 at 19:00 UTC
    Obligatory one-liner, using tac:
    $ export MAX=6;tac YOUR-FILE.txt | perl -lane '$seen{$_}++ or push @ou +t,$_ for @F}{print join(q| |,@out[0..$ENV{MAX}||$#out])' 21 22 23 24 17 18 19
    MAX is set to One less than the number of entries you want(a quirk of zero-based arrays).
    If not set, the entire array is printed.

                    "From there to here, from here to there, funny things are everywhere." -- Dr. Seuss

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11109149]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-25 21:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found