Please sir, write my code.
April 29, 2004 5:01 PM   Subscribe

Any MeFi perl/cgi programmers feel like writing a few lines of code?

I need to add a function to an existing script, which looks like a piece of cake if you know perl.

Given a $newstring and a file of records, each consisting of a count and a string delimited by a space:

201 this.thing
23 that.thing
14 someother.thing
...

I need a function to check whether $newstring already exists in the file. If so, increment the count. If not, add a new record to the file with a count of 1. Finally, sort the file in descending order by count.

Maybe 5 minutes for you. Learning perl first for me. If it would be easier with fixed length counts like 0000201, that is fine, too.

If this is inappropriate for Ask MeFi, my apologies. I'll take it over to answers.google.
posted by Geo to Computers & Internet (10 answers total)
 
#!/usr/bin/perl

# the input string - this just gets it from the command line
$newstring = $ARGV[0];

# temp variables
my $found = 0;
my @lines;

# open filename.txt for reading
open FILE, "<filename.txt";

# for each line in the file
while (<FILE>) {
     # on any line with a number, a space, and $newstring, increment the number, and remember that we found it
     if (s/^(\d+)(?=\s+$newstring)/$1 + 1/e) {$found = 1;}
     # add this line to our array of lines
     push @lines, $_;
}

close FILE;

# if we didn't find it, make the new line
if (!$found) {
     push @lines, "1 $newstring\n";
}

# sort the lines
@lines = sort {int($a) <=> int($b)} @lines;

# open the same file for writing
open FILE, ">filename.txt";
# write out each line to the file
for (@lines) {print FILE;}
close FILE;
posted by whatnotever at 5:51 PM on April 29, 2004


You should change whatnotever's line s/^(\d+)(?=\s+$newstring)/$1 + 1/e to s/^(\d+)(?=\s+\Q$newstring\E)/$1 + 1/e so that regex metachars in $newstring don't mess up the regex.

In any case, next time you have a Perl question, take it on over to PerlMonks where they answer questions like this all the time.
posted by thebabelfish at 5:57 PM on April 29, 2004


Another way of doing it:

$file = "/tmp/infile"; #replace with your input file
$out = "/tmp/outfile";
$found = 0;

open (IN, $file);
open (OUT, ">$out");

while (<IN>) {
  if ($_ =~ /$newstring/) {
     $counter, $string) = split /\s/;
     $counter++;
     $found = 1;
     print OUT "$counter $string\n";temp
     } else {
     print OUT $_;
     }
}

if ($found != 1) { print OUT "1 $newstring\n"; }
close(IN);
close(OUT);
`mv $out $file`;
posted by cmonkey at 6:04 PM on April 29, 2004


Fine, until one CGI process opens and reads the file, another reads and writes it, and then the first overwrites the second's changes... Try this (but I didn't test it!):

use Tie::File;
sub update_count {
  my $newstring = shift;
  tie( my @records, Tie::File => 'filename' )->flock;
  s/^(\d+)(?=\s+\Q$newstring\E)/$1 + 1/e for @records;
  push @records, "1 $newstring" unless $1;
  @records = sort @records;
}
posted by nicwolff at 6:12 PM on April 29, 2004


Response by poster: This is great! I love this place. I don't know why perl seems so impenetrable to me.

And thanks for the link to PerlMonks. That would be the place.

I hope I can return the favor one day. Need any Delphi?
posted by Geo at 6:23 PM on April 29, 2004


Whoops! You wanted descending order, so that last line should be

  @records = reverse sort @records;
posted by nicwolff at 6:52 PM on April 29, 2004


OK, I'm silly - reverse is slow and the default sort won't work - here's one I actually tested:

  @records = sort { (split ' ', $b)[0] <> (split ' ', $a)[0] } @records;
 
posted by nicwolff at 7:01 PM on April 29, 2004


D'oh, MeFi tagulated my flying saucer! It's:

  @records = sort { (split ' ', $b)[0] <=> (split ' ', $a)[0] } @records;
 
posted by nicwolff at 7:03 PM on April 29, 2004


OK, I just have to do the same in python:
#!env python
import sys

def firstsort(this, that):
   return cmp(this[0], that[0])

infile = open(sys.argv[2], 'r')
outfile = open(sys.argv[3], 'w')
inlines = infile.readlines()
outlines = []

for line in inlines:
   count = line.split(' ')[0]
   string = line.split(' ')[1]
   if newstring == string:
      count += 1
   outlines.append((count, string))

outlines.sort()
outlines.reverse()

for line in outlines:
   outfile.write("%s %s" % line)
This is maximized for readability; I hope it's less impenetrable than perl :-)
posted by costas at 9:35 PM on April 29, 2004


Response by poster: I'm tempted to post an APL version. I'm sure it could be done in one line.

The posts have been instructive. All I need to do now is figure out the strange error messages from my ISP.

Thanks everyone.
posted by Geo at 7:21 AM on April 30, 2004


« Older More info needed about maze book   |   Multiracial Places to Live Newer »
This thread is closed to new comments.