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

I am currently working on redoing a past CGI/perl program, and I ran into another problem with my code: how can I reverse the reading of when I read in a text file flatfile db? I need to be able to do this for a news section of this project. Here is the code below:

sub count_lines { $lines = 0; open(FILE, $_[0]) || die "Error opening the file!"; while(<FILE>) { $lines += ($_ =~ tr/\n//); } print "<center>$lines line(s) in [$filepointer].</center><br><br>\ +n"; close (FILE); } sub breakup_lines { open(FILE, $_[0]) || die "Error opening the file!"; &amp;amp;init_vars; while(<FILE>) { $newlength = 0; $cmax = 0; push @list, (split /\|/); $count++; $length = @list; $newlength = $length - $oldlength; #print "length of line: ",$newlength,"<br>"; #print "count: ",$count,"<br>"; $cmax = $length; $oldlength = $length; #print "list array: ",@list; #if ($show{'news'} == 1) { &amp;amp;print_news; #} } close (FILE); } sub init_vars { $count = 0; $next = 0; $dolines = 0; $oldlength = 0; } 1;


I know that the code is not the best chunk in the world, but it is what I have currently, since it is still under development. I would appreciate any help or tips!

Also, just FYI, this .lib is read into by another file, where the print_news subroutine is located.

Andy

Replies are listed 'Best First'.
Re: Reversing (the) reading of a file.
by runrig (Abbot) on Aug 04, 2001 at 06:53 UTC
    While its a fun and educational thing to roll your own (I did it myself in a perl version of tac here), someone's already done that and put it in a module called File::ReadBackwards.

    Oh yeah, and then there's the classic print reverse <> which slurps in the whole file and so is not recommended for large files (and which my tac defaults to on non-seekable filehandles but File::ReadBackwards does not).

Re: Reversing (the) reading of a file.
by mirod (Canon) on Aug 04, 2001 at 06:57 UTC

    You should know it by now, there _HAS_ to be a CPAN module for this ;--)

    In order to read a file backwards you can use...File::ReadBackwards!

    I especially like the tied interface:

    tie *BW, File::ReadBackwards, 'file' or die "can't read file $!"; while( <BW> ) { # do your stuff here ; }
Re: Reversing (the) reading of a file.
by joecamel (Hermit) on Aug 04, 2001 at 06:41 UTC
    Here's one way to do (what I think) you want to do:

    #!/usr/bin/perl -w use strict; # Grab each line in reverse order open (NEWS, "<news.dat") or die "Can't open news file: $!"; my @articles = reverse <NEWS>; close(NEWS); # Your column names, in the order they appear in the file my @colNames = qw(created_time title date); # Store the contents of the file in a data structure (array of hashref +s) my @table = (); foreach my $article (@articles) { chomp $article; my @vals = split /\|/, $article; # Assign each value to its column name, in a hash my %row = (); foreach my $col (@colNames) { $row{$col} = shift @vals; } # Add this row to the table, as a hashref push @table, \%row; }

    Once it's in the table, perlref will help you get what you need out of it. :)

    joecamel

Re: Reversing (the) reading of a file.
by bladx (Chaplain) on Aug 04, 2001 at 21:38 UTC
    Thanks for the help! I was very discouraged earlier since I am still a newbie, and I know that my methods of thinking aren't as good as they will be eventually.

    I still have yet to buy a good perl book (namely: the Perl Cookbook.) so I am still learning as much as I can even about the most basic (to the majority here,) functions. Please bear with me and my ignorance :).

    Andy
Re: Reversing (the) reading of a file.
by bladx (Chaplain) on Aug 04, 2001 at 22:46 UTC
    Ok, thanks to joe_camel, I have this much going so far:

    #!/usr/bin/perl -w use strict; print "Content-type:text/html\n\n"; # Grab each line in reverse order open (NEWS, "<data/news.txt") or die "Can't open news file: $!"; my @articles = reverse <NEWS>; close(NEWS); # Your column names, in the order they appear in the file my @colNames = qw(time_stamp title date author news avatar); # Store the contents of the file in a data structure # (array of hashrefs) my @table = (); foreach my $article (@articles) { chomp $article; my @vals = split (/\|/, $article); # Assign each value to its column name, in a hash my %row = (); print<<HTML; <table width=400 border=1 cellspacing=0 cellpadding=0> HTML foreach my $col (@colNames) { $row{$col} = shift @vals; print<<HTML; <tr> <td> $row{$col} </td> </tr> HTML } print<<HTML; </table> <br> HTML # Add this row to the table, as a hashref push @table, \%row; }


    however, I have no clue how to get it to take out a few of the fields ( the time_stamp, and the avatar fields.) I do not what these fields to show up when the news is posted, even though they are there for sorting later.

    Can anyone show me how to do this? Thanks!

    Andy
      The first thing I would do is decouple the code that gets the data from the code that displays the data. Splitting your code into getters and printers can make life a bit easier for you, especially if you need to display the same data in many different formats. I've tried to show an example of this below by allowing two different views of the data: a default view (in the format I think you were seeking) and a view of the articles by a single author.

      Because each article is stored in a hash, it's easy to print the data however you want. Below, I used an array called @colOrder, that determines which values to display, and determines the order to display them.

      I also used qq~ ~ instead of <<HTML. I find it much cleaner to use, partly because you don't have to worry about the indenting.

      Also, if you're new to Perl, I'd suggest getting Learning Perl and The Camel, before picking up the Cookbook. IMO, learning how the language works will serve you better than seeking out ready-to-use code snippets. (they can be useful and save time if you know what the code does -- just don't use code until you understand how it works). If you're new to programming in general, I highly suggest Code Complete, which will help you write better organized and easier-to-read code -- a valuable skill no matter what language you use.

      Okay, enough of my rambling, here's the code:

      #!/usr/bin/perl -w use strict; ############ # INIT # ############ # Hardcoded params -- you'd probably get these from CGI, though my $view = ''; # In what format to view the data my $author = ''; # If viewing by author, which author? ############ # MAIN # ############ # These will be arrayrefs, so it's easier to pass them to subs my $articles = []; my $colOrder = []; # Print the article list, depending on the chosen view if ($view eq "byAuthor") { $colOrder = [qw.title date news.]; $articles = &get_articles_by_author($author); } else { # Default view $colOrder = [qw.title date author news.]; $articles = &get_articles(); } &print_articles($articles, $colOrder); ############ # SUBS # ############ ### Grabs all of the articles out of a data file and returns them as a +n ### array of hashrefs. This is the only sub you'll use to get the dat +a. If ### later you decide to put the articles in a db, you only have to cha +nge this ### one sub. sub get_articles { # Grab each line in reverse order open (NEWS, "<news.dat") or die "Can't open news file: $!"; my @articles = reverse <NEWS>; close(NEWS); # Your column names, in the order they appear in the file my @colNames = qw(time_stamp title date author news avatar); # Store the contents of the file in a table (array of hashrefs) my @table = (); foreach my $article (@articles) { chomp $article; my @vals = split /\|/, $article; # Assign each value to its column name, in a hash my %row = (); foreach my $col (@colNames) { $row{$col} = shift @vals; } # Add this row to the table, as a hashref push @table, \%row; } return \@table; } ### Get the list of articles by a single author, by getting the full +list of ### articles from the file, and then filtering out everything but the +given author. sub get_articles_by_author { my $author = shift || die "need an author"; my $articles = &get_articles(); # Build a list of articles by the given author my @artByAuth = (); foreach my $article (@$articles) { if ($article->{'author'} eq $author) { push @artByAuth, $article; } } return \@artByAuth; } ### Prints a given list of articles. ### Optional param: the order of columns to display. sub print_articles { my $articles = shift || die "Need an article list"; my $colOrder = shift || keys %{ $articles->[0] }; # Above, if no $colOrder was given, use all of the cols in the tab +le. # Just get the keys in the hash for the first article. # Print the articles print "Content-type:text/html\n\n"; print qq~<table width="400" border="1" cellspacing="0" cellpadding +="0">~; foreach my $article (@$articles) { foreach my $col (@$colOrder) { print qq~ <tr> <td>$article->{$col}</td> </tr> ~; } } print "</table><br>\n"; }


      Of course, other monks will probably be able to suggest improvements that I've missed.

      Have fun,   :)

      joecamel

Re: Reversing (the) reading of a file.
by bladx (Chaplain) on Aug 04, 2001 at 04:59 UTC
    sample flatfile text file that is read in:

    created_time|title|date|author|news|avatar|comments


    just to give you an idea