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

I've just inherited as system where the prog language of choice is Perl. Unfortunately I have little Perl experience but am picking it up fast.

I have a problem which I'm a bit stumped on. Your help would be most appreciated
I have two files (this is a *nix platform)

file1 ------ rabbit 1 hen 0 pig 2 cow 2 sheep 5 file2 ----- rabbit feed 10kg used on 21/10/2005 by consumer1 cow feed 100kg used on 11/11/2005 by consumer1 sheep feed 50kg used on 24/11/2005 by consumer3
I need to be able to update file1 and increment the number in it by 1 if the corresponding animal exists in file2
e.g. rabbit in file1 needs to be incremented to the value 2 because it appears in file2
How do I go about doing this in Perl ?
And yes this is a real agricultural application !

Replies are listed 'Best First'.
Re: update values in one file from another
by GrandFather (Saint) on Dec 07, 2005 at 09:29 UTC
    use strict; use warnings; # Create a test file open outFile, ">", "file1.txt"; print outFile <<end; rabbit 1 hen 0 pig 2 cow 2 sheep 5 end close outFile; #Sample code open inFile, "<", "file1.txt" or die "Unable to read file1.txt. $!"; my @lines = <inFile>; close inFile; my %animals; # build a hash keyed by animal with the count as value map {/(\w+)\s+(\d+)/; $animals{$1}=$2} @lines; # Production code would use file2 rather than DATA while (<DATA>) { /(\w+)/; #Assume animal is always the first thing on the line # Bump animal count - creates new entry if new animal ++$animals{$1} if defined $1; } open outFile, ">", "file1.txt"; for (@lines) { /(\w+)/; printf outFile "%-6s %d\n", $1, $animals{$1}; $animals{$1} = 0; # Tag as printed } #add any new animals to the end of the file for (keys %animals) { next if ! $animals{$_}; printf outFile "%-6s %d\n", $_, $animals{$_}; } close outFile; __DATA__ rabbit feed 10kg used on 21/10/2005 by consumer1 cow feed 100kg used on 11/11/2005 by consumer1 sheep feed 50kg used on 24/11/2005 by consumer3

    Creates a file containing:

    rabbit 2 hen 0 pig 2 cow 3 sheep 6

    DWIM is Perl's answer to Gödel
      This works a treat , thanks.
      I see from the multiple answers that there are many ways of approaching this problem. I guess that's the beauty of Perl ?

Re: update values in one file from another
by Hue-Bond (Priest) on Dec 07, 2005 at 09:59 UTC

    This is my bet at it:

    use warnings; use strict; my %fed; open my $fd, '<', 'file2' or die "open: $!"; $fed{$1}++ while <$fd> =~ m/^(\w+)\s/; close $fd; open $fd, '<', 'file1' or die "open: $!"; open my $fd2, '>', '/tmp/file1.tmp' or die "open: $!"; ## FIXME: tmpf +ile while (<$fd>) { m/^(\w+)\s+(\d+)$/; printf $fd2 "$1\t%d\n", exists $fed{$1} ? $2+1 : $2; } close $fd2; close $fd; rename '/tmp/file1.tmp', 'file1' or warn "rename: $!";

    I've tried editing file1 inplace using $^I and <> but didn't get anything.

    --
    David Serrano

      You can doi it like this:

      use warnings; use strict; # Create a test file open outFile, ">", "file1.txt"; print outFile <<end; rabbit 1 hen 0 pig 2 cow 2 sheep 5 end close outFile; my %fed; $fed{$1}++ while <DATA> =~ m/^(\w+)\s/; local @ARGV = ('file1.txt'); local $^I = '.bak'; while (<>) { m/^(\w+)\s+(\d+)$/; printf "$1\t%d\n", exists $fed{$1} ? $2+1 : $2; } __DATA__ rabbit feed 10kg used on 21/10/2005 by consumer1 cow feed 100kg used on 11/11/2005 by consumer1 sheep feed 50kg used on 24/11/2005 by consumer3

      file1.txt contains:

      rabbit 2 hen 0 pig 2 cow 3 sheep 6

      DWIM is Perl's answer to Gödel

        My error was:

        local $^I = '.bak'; while (<>) { m/^(\w+)\s+(\d+)$/; $2++; ## this

        I guess that would work if I used Tie::File or something like that. Thank you!

        --
        David Serrano

        If I wanted to change the logic of the above code so all values not in $fed{1} were incremented by 1 instead , how would I do this ?
        I tried doing this
        local @ARGV = ('file1.txt'); local $^I = '.bak'; while (<>) { m/^(\w+)\s+(\d+)$/; printf "$1\t%d\n", not exists $fed{$1} ? $2+1 : 0; }
        This only works the first time it's run, after that everything that isn't in $fed{1} is always only set to 1.
        I was trying to get it to increment all other values by 1 each time and set the $fed{1} values to 0
        If I wanted to change the logic of the above code so all values not in $fed{1} were incremented by 1 instead , how would I do this ?
        I tried doing this
        local @ARGV = ('file1.txt'); local $^I = '.bak'; while (<>) { m/^(\w+)\s+(\d+)$/; printf "$1\t%d\n", not exists $fed{$1} ? $2+1 : 0; }
        This only works the first time it's run, after that everything that isn't in $fed{1} is always only set to 1. I was trying to get it to increment all other values by 1 each time and set the $fed{1} values to 0
        I think my logic is screwed up somewhere ?
Re: update values in one file from another
by secret (Beadle) on Dec 07, 2005 at 10:00 UTC

    A simple approach :

    use strict ; use warnings ; my %initial = () ; my %seen = () ; # increment if animal is seen my $new_value ; # Load initial values open FILE, '<', 'file1.txt' or die 'Failed to open file1' ; while ( my $line = <FILE> ) { my ($animal, $count ) = split " ", $line ; $initial{$animal}=$count ; } close FILE ; # Open file to look for new animals open UPDATE , '<', 'file2.txt' or die 'Failed to open file2' ; while ( my $line = <UPDATE> ) { # We assume a special pattern form your example file my $animal = $1 if $line =~ m/^(\w+) feed/ ; $seen{$animal}++ ; } close UPDATE ; # Open result file for writing open FILE, '>','file1.txt' or die 'Failed to open file1 for writing'; foreach ( sort keys %initial) { #Deal with the three possible case : #new animal, previously existing but unseen, or existing and seen if ( defined $seen{$_} and not defined $initial{$_} ) { $new_value = 1 } elsif ( not defined $seen{$_} and defined $initial{$_} ) { $new_value = $initial{$_} } else { $new_value = $initial{$_} + 1 } print FILE "$_ $new_value\n" ; } close FILE ;