http://qs321.pair.com?node_id=92131

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

Fellow monks,

A friend of mine gave me an idea.. One which I've been have a bit of trouble implementing. Here's the scoop:

use File::Slurp; if ($msg =~ /(.*)\+\+/) { my $user = $1; my $karma_file = "karma.txt"; my $results = grep (/^$user/, $karma_file); print "$results\n"; if ($results == 0) { append_file ($karma_file, "$user 1\n"); print "$user now exists\n"; } else { my @lines = split m[$/], read_file ($karma_file); foreach my $line (@lines) { my ($user, $karma) = split /\s/, $line; $karma++; my @without_user = grep (!/^$user/, $results); overwrite_file ($karma_file, @without_user); append_file ($karma_file, "$user $karma\n"); print "$user now has $karma karma\n"; undef $user; } } } } ); }

As you can probably tell, I'm trying to keep a tally on a users 'xp', denoted by the amount of times they were ++'d. My intent is to check a file for the presence of a user, if the user doesn't exist, create it, and append an 'xp' of 0, and if it does exist, grab the users 'xp', increment it, store it, then grab the data from the file excluding that user, overwrite the file, and write the line back to the file with the users new 'xp'.
Sound confusing?

Any suggestions are welcome :)

Replies are listed 'Best First'.
Re: Storing karma
by RhetTbull (Curate) on Jun 28, 2001 at 07:17 UTC
    This sounds like a job for a database. Have you taken a look at DBD::CSVor DBD::RAM? I think one of those solutions would be easier to implement and certainly scale a lot better.
(bbfu) Re: Storing karma
by bbfu (Curate) on Jun 28, 2001 at 07:30 UTC

    First off, you're greping for $user on a list with the single element of ("karma.txt"). That will always return a $results of 0 (I think, or maybe undef) unless the user's name happens to be "karma" (or "karma.txt", or "kar", etc). I'm guessing that's not what you mean.

    I think you're trying to avoid reading in the whole file if the user does not exist. Unfortunately, you can't <update>because you end up reading in the whole file before searching it anyway</update>. So you might as well go ahead and avoid the extra logic...

    (Note! This is untested!!)

    use File::Slurp; if($msg =~ /(.*)\+\+/) { my $user = $1; my @lines = split m[$/], read_file ($karma_file); # Set @data to all lines not starting with $user, # setting $user_line as the last line starting # with $user as a side-effect. Note that do{} # returns the value of the last expression executed, # so the 0 is required. my $user_line = ""; my @data = grep { /^$user/ ? do{$user_line = $_;0} : 1 } @lines; # We already have $user, and redeclaring it causes a warning. # So, instead, we just skip it. my (undef, $karma) = split /\s/, $user_line; ++$karma; push @data, "$user $karma"; overwrite_file($karma_file, @data); }

    Update: I like RhetTbull and Zapawork's suggestions better. :-p

    bbfu
    Seasons don't fear The Reaper.
    Nor do the wind, the sun, and the rain.
    We can be like they are.

Re: Storing karma
by Zapawork (Beadle) on Jun 28, 2001 at 07:30 UTC
    Well here is two thoughts:

    1) Use the dbi library for csv. This way you could just have a flat file database of users with their karama values that could be easily queried and inserted into. Letting the dbi library handle the quering and the data updates without constant file overwrites. This should also reduce the memory footprint of the file as it grows in size. Just use dbi and then connect to the karma.txt as a flat file database. You will have to insert some tabs or commas if you don't currently have them inserted.

    2) Instead of reading and writing the file over and over .. note this is if it is a small file, just read into a memory hash or array, your choice but with the small amount of info to be stored i would say array, and then let the array just overwrite the file at the end of the execution. In this way you would only read and write to the disk once and handle all the rest of the transactions in memory which is much faster and would allow replacements within the array.

    Hope thats helps.

    Dave

Re: Storing karma
by Anonymous Monk on Jun 28, 2001 at 12:40 UTC
    How about (untested)
    open X,'karma.txt';undef$/; my %x=split/\s+/,<X>; $x{$user}++; close X;open X,'>karma.txt'; print X "$_ $x{$_}\n" for keys %x; close X;
      Anonymous Monk seems to have the right idea: Just load the whole database into a hash, increment the user element (it will be created if non-existant), and save the whole thing overwriting the file.

      If the user database gets too large to be handy, an upgrade would be to tie the hash to a database of some kind. There is one that seems universal to perl installations. ( NDBM? )

      use NDBM_File; tie(%x, 'NDBM_File', 'karma.db', 1, 0); $x{$user}++; untie(%x);
      Here the hash keys and values are maintained in the database and some subset is also maintained in the hash itself, but elements are transferred implicitly from the database to the hash (and vice-versa) as needed. The 'untie' line puts all new and changed elements out to the database.

      That last paragraph is clear as mud, hmm.. This page may be of use (or not).

      Amused

      Edit by tye