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

Hi,

I am in a dilemma for which it appears my understanding of the seek and tell functions is lacking.

I am writing code whose purpose is to take a file and if the number of lines of the file is a certain limit, remove the final line. Also, add a line to the beginning of the file.

The procedure I am using is to write the file to an array, check its size and pop if line limit is reached. Then, open the same file, go to position 0, write out the new line, and then write out the contents of the array. Finally, truncate the rest of the file. (I realize I could just delete the file, but want to be able to use - understand - the seek, tell, and truncate functions.)

My dilemma is that after I run my code, the line that I want to be first IS LAST. This caused me to consider that perhaps the seek function KEEPS the file position at 0 (instead of "walking" along what is being written), but wouldn't this then mean the array would appear in reverse order (to be consistent)? (It doesn't.)

Anyway, here is the code and my summary questions are:

Why is the line I intend to be first, last?

Why is the array outputted in sequential order?

Any recommended diagnostics I could have done to solve the problem myself?


Thanks in advance for any assistance.

Tony (o2bwise)

My Code:
#!/usr/bin/perl -w use strict; my @messagesIn; my $MAXSAVE = 12; my $name = "Duane Wade"; my $subject = "My Career"; my $message = "No one ever thought I'd be this good."; flock("messageTest.txt",2); open(FROMFILE,"<messageTest.txt"); while (<FROMFILE>) { chomp $_; push (@messagesIn, $_); } while (@messagesIn > $MAXSAVE) { pop (@messagesIn); } close(FROMFILE); flock("messageTest.txt",8); open (TOFILE,">>messageTest.txt"); flock("messageTest.txt",2); seek(TOFILE,0,0); print TOFILE "$name|$subject|$message\n"; my $counter = 0; while ( $counter < @messagesIn ) { print TOFILE "$messagesIn[$counter]\n"; $counter++; } truncate(TOFILE, tell(TOFILE)); flock("messageTest.txt",8); close (TOFILE); 1;

Replies are listed 'Best First'.
Re: lack an adequate understanding of (at least) the seek function
by jeffa (Bishop) on May 23, 2005 at 20:29 UTC

    While i do appreciate wanting to learn the intricacies of a particular tool, in this case i'd just get a different tool. Tie::File

    use strict; use warnings; use Tie::File; tie my @file, 'Tie::File', 'messageTest.txt' or die "can't open\n"; my $MAX = 5; if (@file > $MAX) { @file = @file[0 .. $MAX - 1]; unshift @file, "new line"; }
    Tie::File even has built-in flocking.

    Thanks to Corion for alerting me that i linked to File::Array ... oops :)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      jeffa,

      Thanks!

      Man, that is too cool!

      o2
      jeffa,

      This is REAL quick (and I am quite a beginner with OO Perl).

      I am wondering if the "complaint" I got for my code (which gave me the desired results) is benign.

      Here's the code:
      #!/usr/bin/perl -w use strict; use warnings; use Tie::File; my $MAXSAVE = 5; my $name = "Duane Wade"; my $subject = "My Career"; my $message = "No one ever thought I'd be this good."; my $myTieObject = tie my @messagesIn, 'Tie::File', 'messageTest2.txt' +or die "can't open\n"; $myTieObject->flock; unshift @messagesIn, "$name|$subject|$message"; if (@messagesIn > $MAXSAVE) { @messagesIn = @messagesIn[0 .. $MAXSAVE - 1]; } untie @messagesIn; 1;


      And here's the complaint:

      untie attempted while 1 inner references still exist at ./messageTest2.pl line 21 <FH> line 16.

      Line 21 is the untie line and Line 16 is the if statement.

      Other than that, I am all set and much obliged!

      Tony

        You are quite welcome o2bwise. The problem is that you are untie'ing the wrong thingy. Try this instead:

        untie $myTieObject;
        And there is no need to have 1; as the last line of a script. That's only necessary for modules. Cheers! :)

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: lack an adequate understanding of (at least) the seek function
by Thelonius (Priest) on May 23, 2005 at 21:46 UTC
    jeffa has a great solution, but just for a learning experience,
    1. flock takes a file handle as its first argument, not a file name. You cannot lock before you open
    2. When you open (TOFILE, ">>messageTest.txt");, you are opening with O_APPEND, which means, as the POSIX standard says, "the file offset shall be set to the end of the file prior to each write". So your seek will be in vain. Theoretically you can still read from other positions if you open it "+>>", although I just got a freaky result when I tried it.
Re: lack an adequate understanding of (at least) the seek function
by mikeraz (Friar) on May 23, 2005 at 20:36 UTC

    Why seek at all in this situation? Why not:

    #!/usr/bin/perl -w # use strict; my @messagesIn; my $MAXSAVE = 12; my $name = "Duane Wade"; my $subject = "My Career"; my $message = "No one ever thought I'd be this good."; flock("messageTest.txt",2); open(FROMFILE,"<messageTest.txt"); while (<FROMFILE> and $cnt++ < $MAXSAVE ) # just quit reading when you + have enough { # chomp; you put the line ending back when you write it so don't +bother taking it off push @messagesIn, $_; } close(FROMFILE); flock("messageTest.txt",8); open (TOFILE,">>messageTest.txt"); flock("messageTest.txt",2); seek(TOFILE,0,0); # shove your message at the start of the array unshift @messageIn, "$name|$subject|$message\n"; # print TOFILE "$name|$subject|$message\n"; # my $counter = 0; # while ( $counter < @messagesIn ) print TOFILE @messageIn; # foreach (@messagesIn) # { # print TOFILE; # } # # truncate(TOFILE, tell(TOFILE)); flock("messageTest.txt",8); close (TOFILE); 1;
    That does what I think you're after.

    Edit by castaway - replaced pre tags with code tags

      mikeraz,

      Thanks, man!

      I learned a few things from your reply and the combination of your reply and jeffa's was real helpful as he showed a whole different approach while your approach was conducive to my attempt.

      Though, I confess to not understanding how excessive line numbers would be removed from the original file. Like if it had 30 lines and a limit was 10, I can see how the array would have the desired ten lines. But, I don't see how the 30 line file wouldn't end up having 40, with the array's "contribution" preceding what is already there.

      I'll test it out.

      Thanks again...

      o2

        You're correct, the code as shown will append. That's easy enough to correct though. Replace the >> with a single > in your open of the output file.