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

HI guys, im new to perl and this is my first post here so go easy, im trying to make a simple message board, ive done ok so far with the code

open(filehandle,">>guestbk.txt") or die("Cannot Open Data File.. Error"); print (filehandle " #!! #l# $message - $from #l# \n!!#\n"); close(filehandle);

and that works fine the >> i know appends to the file, but thats at the bottom, any tips or ideas on how to append to the top of the file? Thanks in advance and excuse my ignorince if nessesary. Cheers, Chris Rowe

Replies are listed 'Best First'.
Re: simple message board
by chromatic (Archbishop) on Oct 24, 2003 at 22:26 UTC

    That'd be prepending, actually. The Tie::File module on the CPAN really the easiest way to accomplish this.

      This sounds like a reasonable solution at the beginning, but once you examined what Tie::File does in this case, the solution is quickly invalidated.

      Try this piece of testing code:

      use Tie::File; use Data::Dumper; my $self = tie @array, "Tie::File", "a.pl"; print Dumper($self); unshift @array, "abcd"; print Dumper($self);

      If you look at the offset array in the dump, you will see that the entire file is loaded to memory (not at a given point, but across the time line)in order to archieve this, which is really a waste, so this task should be rather handed over to OS.

      This is the test result against a copy of the demo program:

      $VAR1 = bless( { 'autochomp' => 1, 'mode' => 258, 'deferred_s' => 0, 'autodefer_threshhold' => 3, 'sawlastrec' => undef, 'defer' => 0, 'dw_size' => 2097152, 'offsets' => [ 0 ], 'deferred_max' => -1, 'autodeferring' => 0, 'recsep' => ' ', 'rdonly' => '', 'memory' => 2097152, 'filename' => 'a.pl', 'fh' => \*Tie::File::FH, 'ad_history' => [], 'autodefer' => 1, 'deferred' => {}, 'autodefer_filelen_threshhold' => 65536, 'recseplen' => 2, 'cache' => bless( [ bless( [ [ 0, $VAR1->{'cache'}, 0 ] ], 'Tie::File::Heap' ), {}, 2097152, 0 ], 'Tie::File::Cache' ) }, 'Tie::File' ); $VAR1 = bless( { 'autochomp' => 1, 'mode' => 258, 'deferred_s' => 0, 'autodefer_threshhold' => 3, 'sawlastrec' => undef, 'defer' => 0, 'dw_size' => 2097152, 'offsets' => [ 0, 6, 22, 41, 43, 88, 110, 135, 157 ], 'deferred_max' => -1, 'autodeferring' => 0, 'recsep' => ' ', 'rdonly' => '', 'memory' => 2097152, 'filename' => 'a.pl', 'eof' => 1, 'fh' => \*Tie::File::FH, 'ad_history' => [], 'autodefer' => 1, 'deferred' => {}, 'autodefer_filelen_threshhold' => 65536, 'recseplen' => 2, 'cache' => bless( [ bless( [ [ 0, $VAR1->{'cache'}, 0 ] ], 'Tie::File::Heap' ), {}, 2097152, 0 ], 'Tie::File::Cache' ) }, 'Tie::File' );

        So what?

        No, seriously. *Something* has to copy the whole file at some point. I don't know of any mechanism that can avoid that without mucking about with inodes. I personally would only do that as a lark and I wouldn't recommend it as a serious solution to anyone asking this question.

        Tie::File is well-maintained, has a nice interface, and is dead-simple to explain. If the original poster starts getting 86400 new messages per day (one hit per second), I'd start worrying about scalability.

        I'm okay with "wasting" a few dozen milliseconds and kilobytes here and there if it makes my life easier. If that's unreasonable, so be it.

Re: simple message board
by sauoq (Abbot) on Oct 25, 2003 at 01:12 UTC

    Why do you want to prepend?

    It's probably wiser to cope with the weaknesses of flat files by avoiding them (the weaknesses) altogether and concentrating on their strengths.

    It is more efficient to read a file backwards (by seeking backwards from the end, reading in a chunk and looking for an end-of-line) than it is to prepend data to a file.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: simple message board
by davido (Cardinal) on Oct 24, 2003 at 22:28 UTC
    One easy way to "append to the top of a file" (or prepend) is to first create a temp file that contains your "append" text. And then append to that the contents of the file that oritinal file. You would then replace the original file with the temp file.

    Of course perlfaq5 lists the previously mentioned method, of using the Tie::File module. :)


    Dave


    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein
      Yeh, thats what i have been trying with little success. I can make the temp file but appending the 2 files together is the hard bit. ive got a book that covers perl and it says to use "cat >> guestbk.txt temp.txt" or to use type if cat on win systems, ive played about with this with no success.
      Do you have any ideas / samle code?
      Thanks
        Using Tie::File is going to be cleaner and more robust than my home-rolled implementation. But I'll show you a home-grown alternative anyway.

        And if your book is telling you that 'cat' is the best solution to prepending something to a file from a CGI script, I'd have to disagree with the book and question its value. "cat" is not as portable, and I just don't like relying on system commands when there is a more Perlish solution.

        As for how to do what I first described, here's one way:

        use strict; use warnings; my $tempfile = "temp.txt"; my $origfile = "guestbook.txt"; my $prepend_text = "Stuff to prepend...\n"; open TEMP, ">$tempfile" or die "Can't create temp file. $!\n"; print TEMP $prepend_text; open ORIG, "<$origfile" or die "Can't read guestbook file. $!\n"; while ( my $line = <ORIG> ) { print TEMP $line; } close ORIG; close TEMP or die "Unable to close output file: $!\n"; rename $tempfile, $origfile;

        But don't just cut-n-paste that snippet, because it doesn't even begin to address important issues like file locking, which is something you really have to be concerned with, particularly when writing to files from a CGI script.

        Also, it doesn't deal with how to generate a temporary file that doesn't already exist, and it doesn't address the portability of the rename function. You might find it necessary, depending on your OS, to use File::Copy and unlink with my method. ...and File::Temp.

        I recommend that you take a look at perlfaq5 and perlopentut as additional reading materials. Writing to files from a CGI script (or any environment where more than one entity might be trying to write to a file at the same time) is a little tricky, and you will save yourself a lot of headaches by understanding the various issues at hand. perlfaq5 includes discussions on file locking, prepending, and creating temporary filenames.

        Update: Now here's an example of using the Tie::File module that everyone keeps talking about:

        use strict; use warnings; use Tie::File; my $string = "Text to be prepended.\n"; my @array; { my $obj = tie @array, 'Tie::File', "guestbook.txt" or die "Can't open the guestbook file. $!\n"; $obj->flock; unshift @array, $string; untie @array; }

        That's it! And the good news is that this example also locks the file so that if two people submit your guestbook form at the same time you won't get a garbled file. As you can see, Tie::File handles a lot of the details for you. You might notice the use of $obj within an enclosing lexical block. The importance isn't immediately obvious, but remember that at the end of the lexical block, $obj falls out of scope, which happens to be the way that Tie::File's documentation suggests you use to unlock the file after closing it with untie.

        Update: If it's likely that your guestbook is going to grow to a pretty good size, and you can't live with the inefficiency of a flat-file growing large, it becomes time to look into database solutions which do scale well, and do make it quite easy to prepend, append, inpend, and unpend (excuse the linguistic liberties) data. Chances are though, that unless you're set to become the next eBay or Amazon, (or perlmonks.org) your guestbook is going to remain small enough that flatfiles are just fine.


        Dave


        "If I had my life to do over again, I'd be a plumber." -- Albert Einstein
Re: simple message board
by chanio (Priest) on Oct 25, 2003 at 16:18 UTC
    I did a similar script. And am also a newbie.

    But I dealed with small files in a special txt directory.

    By using clear multi-coded filenames, I am able to collect just what I need for any situation and then output a single file.

    I see it as a simpler solution. And better work to do for learning about programming (analysis, planning, etc.)

    this sounds good!