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

Hi there , I have this small question , how can I update a file that contains the following :
red blue black
without duplicate entries , if i get a new color as input , I need to go an add it to the end of file , if the color exists then I need to exit and not update the file. thanks

Replies are listed 'Best First'.
Re: updating file
by davido (Cardinal) on Apr 15, 2004 at 20:46 UTC
    Here's an explicit method:

    my $newcolor = 'purple'; my $seen = 0; open my $infile, "<", 'colorfile.txt' or die "Can't open color file for reading.\n$!"; while ( my $color = <$infile> ) { $seen++ if $color =~ /$newcolor/; } close $infile; unless ( $seen ) { open my $outfile, ">>", 'colorfile.txt' or die "Can't open the color file for append.\n$!"; print $outfile "$newcolor\n"; close $outfile or die "Couldn't close the output file.\n$!"; }

    And here's a grep method:

    my $newcolor = 'purple'; open my $infile, "<", 'colorfile.txt' or die "Can't open color file for input.\n$!"; my $seen = grep { /$newcolor/ } <$infile>; close $infile; open my $outfile, ">>" 'colorfile.txt' or die "Can't open color file for output.\n$!"; print $outfile "$newcolor\n" unless $seen; close $outfile or die "Can't close the output file.\n$!";

    You could also just open the file once, for read/write access, but that can get a little confusing if you don't do it right. See perlopentut for details.

    The above methods just open the file for reading, scan through it to see if the color exists. If it doesn't, reopen the file for appending, and append the new color to the file.

    Update: Here's another solution that uses Tie::File. It doesn't scale as well, it's definately convenient:

    use strict; use warnings; use Tie::File; my $newcolor = 'purple'; tie my @array, 'Tie::File', 'colorfile.txt' or die "Can't tie color file.\n$!"; push @array, "$newcolor\n" unless grep { /$newcolor/ } @array; untie @array;


    Dave

      A nit...these scripts won't update the file if $newcolor is a substring of a color already in the file. For example if the file has:
      red-orange bluegreen
      and you try to add one of the colors red, blue, or green, your script won't add it.

      The regexp should probably be anchored and chomp used:

      chomp($color); $seen++ if $color =~ /^$newcolor$/;
      or eq could be used instead:
      chomp($color); $seen++ if $color eq $newcolor;
Re: updating file
by bart (Canon) on Apr 16, 2004 at 03:23 UTC
    If this were for in-memory handling, instead of in a file, I'd think of Tie::IxHash. That implements a "hash" that still acts like a hash in all respects, thus for example, each key must be unique; but in addition, it preserves the order of addition, when you list its keys/values. Here, I only use the keys.
    my @color = qw(red blue black); use Tie::IxHash; tie my(%color), Tie::IxHash; # Existing colors: foreach(@color) { $color{$_}++; } # Attempt to add some: foreach(qw(purple blue green red)) { $color{$_}++ or print "Ooh, that's a new one: $_\n"; } print "I've got:\n"; print " * $_\n" foreach keys %color;
    Result:
    Ooh, that's a new one: purple
    Ooh, that's a new one: green
    I've got:
     * red
     * blue
     * black
     * purple
     * green
    
    As you can see, it's easy to spot new additions, dupes are prevented, plus the original order of addition is preserved.

    If this is for a file, you can just append the new color addition to the end of the file, when you encounter a new one. You then don't even need to use Tie::IxHash — a plain hash will do.