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

Hello guys and I am very appreciative to anyone whom might can give some direction on how to handle a current situation that I am dealing with. Please allow me to explain the best way that I can. I have a script that automatically assigns a number by excuting this code:

my $range = 200; my $minimum = 1000000000; my $jobNum = int(rand($range)) + $minimum;

When the value is assigned to $jobNum then I need to go an extra step in determining that the number has never been previously used. Of course, with a range that I have chosen, most likely it won't occur, but you can't assume either. The previously used $jobNum(s) reside in a file called aj.db which is a basic text file that the data is held in from html forms, with  | separators. I want to be able to search this file by opening it temporary (within the running script), search for the randomly assigned String, that is assigned at that moment, against the previously assigned Strings within the aj.db database file. <If> the String cannot be found then allow the current randomly assigned String to be used. Of course, if the String of numbers have been used before, then loop back to get a new random number and check again until one is found that has not been used. I'm sure there is a simple solution for this but I have not found none yet and cannot understand fully how this can be done. I have no current code to present in my testing to see if I can do this on my own. If anything, point me toward some ideas that i can try so I might achieve this own my own. Again, any help would be appreciated.

Thank you

Replies are listed 'Best First'.
Re: Testing numeric strings
by toolic (Bishop) on Jul 20, 2011 at 18:15 UTC
    Another approach might be to read in all the used numbers before selecting a new one. You would need to optimize things here, but create a list of unused nums from your list of used nums, then randomly select a num from the unused list:
    use warnings; use strict; use Acme::Tools qw(minus); use List::Util qw(shuffle); my @alls = 0 .. 9; my @useds = qw(1 2 5 8); # from your aj.db my @unuseds = shuffle(minus(\@alls, \@useds)); my $new_num = $unuseds[0];
      There is other data in the file aj.db that has to be sorted through. This database file looks similar to this:
      1000000163|Jim|Tue Jul 19 14:06:09 2011 1000000066|Kris|Tue Jul 19 14:28:46 2011

      This file is created after submitting the data through forms on html pages. It's all CGI script driven. The numbers need to be parsed out for checking by code that is embedded in the CGI script that is the actual script where the data is entered into the forms.

Re: Testing numeric strings
by jethro (Monsignor) on Jul 20, 2011 at 18:36 UTC

    I assume you meant your range to be 100000000 and not the minimum, contrary to your script.

    Also may I ask if it wouldn't be better to just use a counter for job ID. If you don't want the jobNum to be guessible, maybe a scheme like this could be used:

    my $range= 10000; open(my $file,"<","counter.txt") or die "Could not read counter file: +$!\n"; my $counter= <$file>; $counter++; $jobNum= $counter*$range + int(rand($range)); open($file,">","counter.txt") or die "Could not write to counter file: + $!\n"; print $file $counter;

    As you can see the jobNum now has a random part in the lowest 4 digits and a counter in the higher digits which makes every jobNum unique but not guessable.

    Because there is a biggest possible integer in perl (on many machines 2^32) you might add code to reset the counter if it gets bigger than 100000, but that is not a hard limit, the randomness of your number just gets diminished slowly as your number gets bigger (after you stepped over the 2^32 limit)

    If you want to pursue your version, use this script as inspiration how to read and write to a file. Hint: To append to a file, use ">>" instead of ">"

Re: Testing numeric strings
by onelesd (Pilgrim) on Jul 20, 2011 at 18:38 UTC
    This won't give you random numbers, but it will keep them unique if you increment $max_id as needed. If you will have more than 1 thread you will need to do some locking.
    open my $fh, "< $file" ; my $max_id = 0 ; while (<$fh>) { if ($_ =~ /^foo|(\d+)|bar$/) { $max_id = $1 if $max_id < $1 ; } } my $next_id = $max_id + 1 ;
      Here is the actual script:
      #!/usr/bin/perl ############################ # Get data from GET & POST # ############################ &parse_form; ################ # VARS defined # ################ $theDate = localtime; ################################## # Random numbers to be generated # ################################## my $range = 200; my $minimum = 1000000000; my $jobNum = int(rand($range)) + $minimum; #NEED CODE HERE TO DO SOME CHECKING AGAINST aj.db (Active Jobs) ################################## # Print out the page for editing # ################################## print "Content-type: text/html\n\n"; print " <html> <head><title>PC Job Tracker - Add Job [$input{'database'}]</title> </head> <body bgcolor='#b39f75'> <form action='editjob.cgi' method='GET'> <input type='hidden' name='action' value='add'> <input type='hidden' name='database' value='$input{'database'}'> <font face='verdana' color='#m3t789' size='2'> <h1><u>PC Job Tracker | Add PC Job</u></h1> <table> <tr><td><font size='4'>Job #:</font></td><td><input type='text' name=' +njob' value='$jobNum'></td> </tr> </table> <br> <h2><u>Client Data</u></h2> <table> <tr><td><font size='4'>First Name:</font></td><td><input type='text' n +ame='fname'></td><td>&nbsp;&nbsp;&nbsp;</td><td><font size='4'>Last N +ame:</td></font><td><input type='text' name='lname'></td><td>&nbsp;&n +bsp;&nbsp;</td><td><font size='4'>Phone Number:</font></td><td><input + type='text' name='nphone'></td> </tr> </table> <br> <font face='verdana' size='3' color='#m3t789'> <h2><u>Computer Info</u></h2> <table> <tr><td><font size='4' color='black'>Machine:</font></td> <td> <select name='nmachine'> <option value='none'>------------------------- <option value='homebrew'>Home Built <option value='acer'>Acer <option value='apple'>Apple <option value='compaq'>Compaq <option value='dell'>Dell <option value='emachine'>eMachine <option value='gateway'>Gateway <option value='hp'>HP <option value='ibm'>IBM <option value='lenovo'>Lenovo <option value='pb'>Packard Bell <option value='toshiba'>Toshiba </select> </td> <td>&nbsp;&nbsp;&nbsp;</td> <td>&nbsp;&nbsp;&nbsp;</td> <td><font face='verdana' size='4'>Date of Arrival: <input type='text' +name='ndoa' value='$theDate'></td> </table> <br> <font face='verdana' size='5'><u><b>Comments</b></u></font> <br> <textarea name='nnotes' wrap='physical' rows='6' cols='50'> </textarea> <table> <tr> <td> <input type='submit' value='Submit'> </form> </td> <td>&nbsp;&nbsp;&nbsp;</td> <td> <form action='main_view.cgi' method='GET'> <input type='submit' value='Cancel'> </form> </td> </tr> </table> </font> </body> </html> "; ######################################## # Code to get the data from GET & POST # ######################################## sub parse_form { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); if (length($buffer) < 5) { $buffer = $ENV{QUERY_STRING}; } @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $input{$name} = $value; } }
Re: Testing numeric strings
by zek152 (Pilgrim) on Jul 20, 2011 at 20:54 UTC