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

I have a file that contains a single integer number which is used as a counter. What I'd like to do is open the file, read the number, increment by 1, write the result to the original file for next time. Seems simple enough, but for the life of me I can't get it to work.

Any suggestions? The usual result is the file written is empty. I have tried so many different ways to do this, that I have really confused myself.... one version looks like the following (don't laugh)

open (RECORD_NUMBER, "counter_data.dat") or die; $count = <RECORD_NUMBER>; $count = $count + 1; print RECORD_NUMBER "$count"; close (RECORD_NUMBER) or die;

Replies are listed 'Best First'.
Re: File I/O
by davidrw (Prior) on Jan 17, 2007 at 20:44 UTC
    your attempt only opens the file for read, not write.
    open RECORD_NUMBER, '<', "counter_data.dat" or die; $count = <RECORD_NUMBER>; close RECORD_NUMBER; $count++ open FILE, '>', "counter_data.dat" or die; print FILE $count; close FILE;
    Have to ask, WHY do you need to do this? (there may be a far better/more robust way to accomplish your specifc task)

    alternative cmd-line way (see perlrun for the switches):
    perl -i -pe '++$_' counter.dat

      Maybe, maybe not. For simple jobs run from a cron or similiar, where locking may not be an issue, a simple counter file can often be the most suitable solution (esp. where ease of distrib. is an issue).

      IMHO, needless to say, but KISS.

      map{$a=1-$_/10;map{$d=$a;$e=$b=$_/20-2;map{($d,$e)=(2*$d*$e+$a,$e**2 -$d**2+$b);$c=$d**2+$e**2>4?$d=8:_}1..50;print$c}0..59;print$/}0..20
      Tom Melly, pm@tomandlu.co.uk
      First: Thanks to everyone who responded--so quickly!! This is sort of the blind leading the blind...I wrote a short PERL CGI script to give a truly non-programming person the ability to use a simple form that accepts input and creates an html file (which builds a table of data). My first crack at the CGI has very limited error checking. As I look to improve it, I figured I could capture the date and also increment the record entry counter so Patty only has to worry about a few entry fields. She is the only person besides myself that will probably ever use this. I guess I managed to do a couple things right...my die statements actually have a descriptive string with them. The way I opened the file threw me because a couple variations "did" write, but was incorrect. thanks again, I'm confident this will work. Can't wait to try it out in the office.
      your attempt only opens the file for read, not write.
      open RECORD_NUMBER, '<', "counter_data.dat" or die; $count = <RECORD_NUMBER>; close RECORD_NUMBER; $count++ open FILE, '>', "counter_data.dat" or die; print FILE $count; close FILE;

      I see most people answered along these lines. It is worth mentioning open '+<' mode too, although in that case one would have to seek as well.

Re: File I/O
by jettero (Monsignor) on Jan 17, 2007 at 20:44 UTC

    This feels like a job for pi!

    bash$ perl -pi .oops -e 's/(\d+)/$1+1/e' filename.ext

    Just what are p, i, and e? Check the perlrun page for further information.

    Garh!! davidrw seems to have beaten me by several seconds on this. In answer to his question:

    I would say the longer self contained version is probably more maintainable — depending on where the application is used/stored. The pi-way is fine for a one time addition to one's own crontab, but for full platform compatability and for the sake of future readers (who may not know perl all that well), the longer version may be better. It depends.

    -Paul

Re: File I/O
by shmem (Chancellor) on Jan 17, 2007 at 23:46 UTC
    Each line needs correction ;-)
    open (RECORD_NUMBER, "counter_data.dat") or die;

    With 2-argument open, not specifiying the open mode, you open the file for input only. Either open it for input, best done explicitly with 3-argument open, close it after reading, re-open it in write mode then - or open it for update. for die see below.

    $count = <RECORD_NUMBER>;

    The line you just read might contain a line feed character at the end. Use chomp: chomp($count = <RECORD_NUMBER>);

    $count = $count + 1;

    Better written as $count++; - see perlop.

    print RECORD_NUMBER "$count";

    Useless variable interpolation within double quotes. print RECORD_NUMBER $count; is just fine. But then, you might want to append a "\n".

    close (RECORD_NUMBER) or die;

    Big plus for checking the return value of close. It's rarely seen, but should be done on every close. You might say or die "Can't close filehandle RECORD_NUMBER: $!\n" - so you know what went wrong.

    I would write your code probably as

    open (RECORD_NUMBER, '+<', "counter_data.dat") or die; chomp($count = <RECORD_NUMBER>); $count++; seek RECORD_NUMBER, 0, 0; print RECORD_NUMBER $count,"\n"; close (RECORD_NUMBER) or die "Canīt close filehandle RECORD_NUMBER: $! +\n";

    The seek call rewinds the file to the beginning. If I did not, the new number would just be written at the actual file position - after the line read in.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: File I/O
by jwkrahn (Abbot) on Jan 17, 2007 at 21:43 UTC
    The simplest method is to use Tie::File
    use Tie::File; tie( my @count, 'Tie::File', 'counter_data.dat' )->flock; $count[ 0 ]++; untie @count;
Re: File I/O
by rhesa (Vicar) on Jan 18, 2007 at 00:14 UTC
    The Perl FAQ 5 page contains the definitive answer at "I still don't get locking. I just want to increment the number in the file. How can I do this?".

    Another approach would be to use File::CounterFile.

Re: File I/O
by sgifford (Prior) on Jan 17, 2007 at 22:09 UTC
    Keep in mind that you'll need to use file locking if there's any chance more than one copy of the program could be run at the same time.
Re: File I/O
by lokiloki (Beadle) on Jan 17, 2007 at 22:51 UTC
    The thing you are forgetting is to open the file again, but this time using > to indicate you want to write out...
    open (RECORD_NUMBER, "counter_data.dat"); while (<RECORD_NUMBER>) { $count = $_; } close RECORD_NUMBER; open (RECORD_NUMBER, ">counter_data.dat"); print RECORD_NUMBER $count++; close RECORD_NUMBER;
Re: File I/O
by perlex (Initiate) on Jan 17, 2007 at 23:30 UTC
    try to close file after you read element and then open for writing, you opened it for reading only in your example
Re: File I/O
by perlex (Initiate) on Jan 17, 2007 at 23:33 UTC
    try to close file and open for writing, you opened it for reading only in your example
Re: File I/O
by webfiend (Vicar) on Jan 19, 2007 at 22:24 UTC

    Remember what everybody else said, but I thought I'd point that you would have received an informative message if you had used strict, warnings and diagnostics.

    I created a 'counter_data.dat' file with the single character '1' in my current directory, tuned the code a tiny bit (see below), then ran the script. Here's what I got.

    Filehandle RECORD_NUMBER opened only for input at io.pl line 8, <RECORD_NUMBER>
            line 1 (#1)
        (W io) You tried to write on a read-only filehandle.  If you intended
        it to be a read-write filehandle, you needed to open it with "+<" or
        "+>" or "+>>" instead of with "<" or nothing.  If you intended only to
        write the file, use ">" or ">>".  See perlfunc/open.
    
    
    

    Here is your sample code with the changes I made.

    use warnings; use strict; use diagnostics; open (RECORD_NUMBER, "counter_data.dat") or die; my $count = <RECORD_NUMBER>; # Lexical declaration to keep 'strict' ha +ppy. $count = $count + 1; print RECORD_NUMBER "$count"; close (RECORD_NUMBER) or die;

    I know it can be annoying to include those use statements in all of your code, but it really is helpful. See Use strict warnings and diagnostics or die for a lot of information on helpful ways to make Perl tell you what it's doing.