Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Filehandles vs Uninitialized Values in pattern match

by Flubb (Acolyte)
on Nov 23, 2007 at 15:28 UTC ( #652593=perlquestion: print w/replies, xml ) Need Help??

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

Hullo Venerable Monks:
The program below opens a file, reads the last two lines, looks for a number, increments that number, then (I hope) appends the new lines to the bottom of the file. When I open $file for reading (and only reading) the program works, and prints out the new line to the screen. Making it read/write (+>>$file) gives me:
 Use of uninitialized value in pattern match (m//)...at line 15
I'm not sure why the simple addition of making the file read and writable empties my $line1 variable (opening with >$file destroys the file completely). Any clues?
Full code bit:
------------------------
#!/usr/local/bin/perl -w use strict; my($newline); my($file) = "d:\\softdev\\Pserver.txt"; my(@lines); my($lines); my($line1); my($line2); open (DAT, "+>>$file") or die "$!"; @lines = <DAT>; $line1 = $lines[-1]; $line2 = $lines[-2]; if ($lines[-1] =~ /(\d)/){ $newline = ($1 + 1); print "$newline\n"; $line1 =~s/(\d)/$newline/; print "$line1\n"; print DAT "$line1";} close DAT;
-------------------------
(It's probably ugly code, but I'm self-taught and have no Compsci background :F) Cheers, Flubb

Replies are listed 'Best First'.
Re: Filehandles vs Uninitialized Values in pattern match
by Sidhekin (Priest) on Nov 23, 2007 at 16:04 UTC

    The +>> mode puts the file position initially at eof, so there is nothing left to read. Your @lines will be empty. (And hence, $lines[-1] etc will be undefined.)

    If I read your intentions correctly, you seem to be missing just a seek DAT, 0, 0 before you read from the file. (See seek; in short it sets file position to start of file.)

    (The file position will be advanced as you read, so ending up at eof afterwards, but even if it wasn't, opening with +>> makes sure Perl will seek to eof before starting to output anything on the handle.)

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

Re: Filehandles vs Uninitialized Values in pattern match
by johngg (Canon) on Nov 23, 2007 at 16:18 UTC
    You might find it useful to have a look at Tie::File. This allows you to treat a file just as if it was an array. Thus, adding new lines at the end of the file is as simple as pushing onto the array. If the file is large you might not get great performance so YMMV.

    I hope this is useful.

    Cheers,

    JohnGG

      If the file is large you might not get great performance so YMMV.
      That's probably not going to be much of an issue as (quoth the manual)

           The file is not loaded into memory, so this will work even for gigantic files.

      I don't know if Tie::File seeks from the end for large/negative indexes, but it's surely possible for it to do so.

      I just read the code... please ignore.

      -David

Re: Filehandles vs Uninitialized Values in pattern match
by naikonta (Curate) on Nov 23, 2007 at 16:35 UTC
    The two subroutines in the code below achieve the same thing in different way. As others have pointed out, the mode +< (or O_RDWR with Fcntl module) is the one you need instead of +>> (the latter is still possible, just harder) for the first sub. The second sub uses Tie::File module that makes file operation is as simple as array operation.

    The code (file locking is omitted intentionally):

    #!/usr/bin/perl use strict; use warnings; my($rdwr_file, $tie_file) = @ARGV; with_rdwr(); with_tiefile(); sub with_rdwr { open DAT, "+<$rdwr_file" or die "can't open $rdwr_file: $!\n"; my $last = (<DAT>)[-1]; if ($last =~ /(\d+)/) { my $new = $1 + 1; seek DAT, 0, 2 or die "Can't seek in $rdwr_file: $!\n"; print DAT $new, "\n"; print "new value for $rdwr_file: $new\n"; } close DAT; } sub with_tiefile { use Tie::File; my @lines; tie @lines, 'Tie::File', $tie_file or die "can't tie $tie_file: $! +\n"; my $last = $lines[-1]; if ($last =~ /(\d+)/) { # sure, testing on $lines[-1] saves a line my $new = $1 + 1; push @lines, $new; print "new value for $tie_file: $new\n"; } untie @lines; }

    The run:

    $ echo 0 > rdwr $ echo 0 > tie $ cat rdwr 0 $ cat tie 0 $ perl prog.pl rdwr tie new value for rdwr: 1 new value for tie: 1 $ cat rdwr 0 1 $ cat tie 0 1 ... and some executions later.. $ cat rdwr 0 1 2 3 4 5 $ cat tie 0 1 2 3 4 5

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: Filehandles vs Uninitialized Values in pattern match
by HuckinFappy (Pilgrim) on Nov 23, 2007 at 16:21 UTC
    When you use the line:
    open (DAT, "+>>$file") or die "$!";
    You aren't just opening the file read/write, you're opening it read/write in *append* mode. That means the pointer is at the end of the file when you open it. So when you read it, you don't get anything. There are a few things you can try here to learn a little more. Here's a little debugger session I played with that might help you get this working correctly. There's also some other things I do differently here that you may wish to adopt as standard practices, such as using a lexical filehandle and 3-argument form of open:
    DB<1> open $fh, '+>>', 'test.dat' + + DB<2> if ( eof $fh ) { print "Don't try to read, you're at the end!\ +n" } + Don't try to read, you're at the end! DB<3> seek $fh, 0, 0 + + DB<4> if ( not eof $fh ) { print "Read on, McDuff!\n" } + + Read on, McDuff! DB<5> @lines = <$fh> + + DB<6> x @lines DB<7> chomp @lines + + 0 'First Line' 1 'Second Line' 2 'Last Line'
    Hope this helps, ~Jeff
Re: Filehandles vs Uninitialized Values in pattern match
by zer (Deacon) on Nov 23, 2007 at 16:10 UTC
    '+<' is a better option. It will read the file as it normally would then leaves the file pointer at the end so you can append.

    UpdateSidhekin is right in saying that +>> is clearer to read, however seek can be just as confusing if youve never dealt with it before.

      '+<' is a better option

      +< is another option, to be sure, but better? +>> states your intentions to read and append. +< leaves it open whether or not you intend to overwrite any of the original contents.

      It would save you a seek, sure, but at a cost of clarity. TIMTOWTDI, indeed, but I think Flubb had the right idea.

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

Re: Filehandles vs Uninitialized Values in pattern match
by angiehope (Pilgrim) on Nov 23, 2007 at 16:16 UTC
    Hi!
    Reading the perldoc documentation on open, I found that "+<" was recommended for reading from and writing to files, as "+>" would clobber the file first.
    open (DAT, "+<$file") or die "$!";
    worked on my linux machine (I haven't tested this on Windows though).
    Have a nice weekend!
Re: Filehandles vs Uninitialized Values in pattern match
by Flubb (Acolyte) on Nov 26, 2007 at 12:11 UTC
    Thanks everyone :) I'd add kudos points if I could.
    The "+<" option definitely works. I'll have a check on the others later.
    o/ Flubb

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2021-11-29 14:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?