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

I am new to PERL/cgi and am trying to find a line in a data file that matches $email and then replace the 'nodata' part of the string that matches with $secondary. My code below does all of that, but isn't writing it to the file. I don't want duplicates, I just want what is in the file to be updated. Can you help me?
sub addSecondary($$) { my ($email, $secondary) = @_; open (MYFILE, 'registration.dat'); while (<MYFILE>) { chomp; my $line = $_; if($line =~ /$email/) { $line =~ s/nodata/$secondary/gi; } } close (MYFILE); }

Replies are listed 'Best First'.
Re: Appending to a file.
by tobyink (Canon) on May 07, 2012 at 16:02 UTC

    Before I say anything else, I'll warn you against that little ($$) thing. That's called a "prototype" and it doesn't do what you think it does. In most situations, prototypes are unhelpful.

    Anyway, your problem is that you read each line of the file into memory, do something with it, and then don't write it anywhere. What you want to do is be opening both an input filehandle and an output filehandle, and writing the line to the output filehandle when you're done with it. Something like this:

    sub addSecondary { my ($email, $secondary) = @_; open my $input, '<', 'registration.dat'; open my $output, '>', 'registration.tmp'; while (<$input>) { chomp; my $line = $_; if ($line =~ /$email/) { $line =~ s/nodata/$secondary/gi; } print $output "$line\n"; } close $input; close $output; unlink 'registration.dat'; rename 'registration.tmp' 'registration.dat'; }

    (Note to pedants: for cross-platform compatibility unlink 'filename' is better written as 1 while unlink 'filename' because some operating systems allows multiple versions of the same file to be stored.)

    There are various modules that make line-by-line file editing easier. Corion mentioned Tie::File. I happen to be a fan of File::Slurp. Here's an example using that...

    use File::Slurp qw(:edit); sub addSecondary { my ($email, $secondary) = @_; edit_file_lines { s/nodata/$secondary/gi if /$email/ } 'registration.dat'; }
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      Wow thank you. I am used to vb and c#, not PERL. It is so different to me. I changed it to
      sub addSecondary() { my ($email, $secondary) = @_; use Tie::File; tie @array, 'Tie::File', 'registration.dat'; for (@array) { s/0;nodata/1;$secondary/gi if /$email/ } untie @array; }
      And it works now. What I don't get is why s/0;nodata/1;$secondary/gi if /$email/ works. There is no semicolon at the end and it is not like any function I have seen. I am used to something = something type notation. How does it even know what to look at and compare?
        for (@array) { s/0;nodata/1;$secondary/gi if /$email/ }

        This would be (essentially) shorthand for:

        for my $_ (@array) # 'my $_' is a little bit of a fudge { $_ =~ s/0;nodata/1;$secondary/gi if $_ =~ /$email/ }

        See perlvar for a little more explanation.

        --MidLifeXis

        There's a global variable (though in recent versions of Perl it can be made more local on request) in Perl called $_. Many Perl functions, operators and looping constructs operate on $_ if you don't give them another variable to operate on instead.

        For example, rather than:

        foreach my $item (@array) { print($item); }

        You can take advantage of the fact that the default name used by foreach is $_, and the default thing printed by print is $_, and instead write:

        for (@array) # note "for" and "foreach" are synonyms { print; }

        Or just:

        print for @array;

        Applying the same logic to your loop, the for is assigning each item in @array to $_; and if /$email/ is shorthand for if $_ =~ /email/; etc.

        perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

        xavierarmadillo: I see that in your latest posting the substitution
            s/nodata/$secondary/gi
        has become
            s/0;nodata/1;$secondary/gi
        adding '0;' and '1;' to the mix. Maybe this is intentional, but I just thought I'd mention it.

        Another point is that you are now defining the  addSecondary function with an empty prototype, meaning that the function takes no arguments — and then you pass it two arguments! This suggests to me that you are calling the function in a way that causes Perl (not PERL) to ignore prototypes. If prototypes do nothing in your program, or you don't fully understand them, or both, why bother to use them?

        The final and perhaps most important point is that it is wise always to enable warnings and strictures at the beginning of a program, and as you are a newcomer to Perl, to enable diagnostics. So your program would begin with the following lines:
            use warnings;
            use strict;
            use diagnostics;  # for good measure
        See warnings, strict and diagnostics.

Re: Appending to a file.
by Corion (Patriarch) on May 07, 2012 at 15:45 UTC

    Take a look at the following options:

    Tie::File, which lets you treat a file just like an array.

    perlvar on $^I for how to make Perl edit a file in-place.

      Reading about the tie:file makes some sense. It looks like the array addresses the memory location of the file and it works the same as an array. I don't have any clue what push, new recs...., pop, unshift, shift, or splice do.

        Mitigating cluelessness.
        That's what Perl's documentation is there for.

        • push
        • "new recs"? Perhaps you'd better re-read what puzzled you in the first place so you can quote it accurately.
        • pop
        • unshift
        • shift
        • ...and for splice... try Super Search with splice as your argument... or read a primer such as "Learning Perl."
Re: Appending to a file.
by locked_user sundialsvc4 (Abbot) on May 07, 2012 at 20:35 UTC

    Maybe over the past three decades I have become known as “the odd man out,” but if that be so, then so be it.   It seems to me that you are approaching this file, registration.dat, as though it had somehow become a tablet fixed in stone, writ by the hand of a god.   Could this “flat file” have at this point become ... a database?   If so, then perhaps the instant requirement is merely a bellwether of a larger and more generalized case?   Perhaps this registration file should as quickly as possible become a registration database table ... which any software process (anywhere in the enterprise, on any platform) could now simultaneously update.

    If this is indeed the case, then you will have instantaneously replaced the extant question of “how did the chicken cross the road?” by the installation of a full-featured antimatter transporter network.