Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Random Text: There's got to be an easier way

by Hero Zzyzzx (Curate)
on Jun 03, 2001 at 21:06 UTC ( [id://85330]=perlquestion: print w/replies, xml ) Need Help??

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

I want to create an eight character random ID consisting of lowercase letters and the numbers 2 to 9 (34^8= 1.7 trillion combinations). The reason I'm only using lowercase and excluding 0 and 1 is because the ids are meant to be relatively human readable, they are going to be order ids for a shopping site.

The following code does what I want, but I have this creeping feeling that there's an easier, more memory efficient way to do this. Also, using the list constructor operator with characters (a..z, A..Z) directly in the array causes my script to flame out, saying "bareword found while using strict." I think I'm just screwing up the syntax.

Thoughts?

#### Create random cart id my @seed; my $a= "a"; my $b= "z"; @seed=($a..$b,2..9); my $rand; foreach(0..7){ $rand.=$seed[rand(@seed)]; } #### end of random cart id

Replies are listed 'Best First'.
Re: Random Text: There's got to be an easier way
by Beatnik (Parson) on Jun 03, 2001 at 21:14 UTC
    The Cookbook mentions this on page 52:
    my @chars = ("a".."z","A".."Z"); my $random_string = join("",@chars[map{ rand @chars} (1..8)])); #8 being the number of chars to randomize ofcourse
    Greetz
    Beatnik
    ... Quidquid perl dictum sit, altum viditur.
      Beatnik nailed it, but he left out the numerals 2 through 9 and added an extra parenthesis to the second line. The exact solution is thus:
      use strict; my @chars = ("a".."z","A".."Z", 2..9); my $random_string = join("",@chars[map{ rand @chars} (1..8)]); #8 being the number of chars to randomize ofcourse print "$random_string\n";
        The code described in the Cookbook also includes some non-alphanumeric chars...
        I'm pretty confident the code mentioned above is faster than other stuff (but then again, I didn't write it so that's normal).

        BTW you left out 1 :)

        Greetz
        Beatnik
        ... Quidquid perl dictum sit, altum viditur.

      Thanks!

      Funny. It must have just been the quotes that I was missing.

      Another reason to read the Cookbook, which I just recently purchased (along with Programming Apache Modules with Perl and C)

      Have I got some reading (and re-reading) to do. . .

Re: Random Text: There's got to be an easier way
by VSarkiss (Monsignor) on Jun 03, 2001 at 21:28 UTC
    Well, your @seed array is only 34 elements long -- which is not a big deal. Basically, you're looking for something that maps the range of numbers returned from the rand function to your desired set. If you had a less complicated filter (say, "any printable character"), then you could use an algorithmic approach, but for this small size, a lookup array is probably the best fit.

    As for using strict, you just have to quote the letters in the range. Here's your same code a little shorter:

    my @seed = ('a'..'z', 2..9); my $rand = ''; $rand .= $seed[rand(@seed)] foreach 1..7;
    HTH

      Again, the beautiful thing about perl is that there can be many different ways to do the same thing simply and elegantly. Thanks to all for the quick help!!

      This is pretty much the code what I was looking at, though I need to learn to use "join" as beatnik pointed out above.

      The final version for me, unless someone gives me a compelling reason to use "join".

      my @seed=('a'..'z',2..9); my $rand=''; foreach(0..7){ $rand.=$seed[rand(@seed)]; }
        Benchmark, nuff said

        Greetz
        Beatnik
        ... Quidquid perl dictum sit, altum viditur.
(dws)Re: Random Text: There's got to be an easier way
by dws (Chancellor) on Jun 04, 2001 at 00:26 UTC
    ... the ids are meant to be relatively human readable ...

    If the ids might be spoken (say, to an agent over the phone), you might consider producing them according to an easy-to-speak, easy-to-hear pattern. Something like

    Consonant-vowel-consonant-digit
    Consonant-vowel-consonant-digit
    By using a predictable pattern, miscommunication is minimized. Since an agent knows to listen for a digit in the fourth and eighth position, zero and one are unambiguous.

    If you're concerned about accidentally generating "dirty" words, try

    Consonant-consonant-vowel-digit
    Consonant-consonant-vowel-digit
    This lessens the odds.

(dkubb) Re: (2) Random Text: There's got to be an easier way
by dkubb (Deacon) on Jun 03, 2001 at 23:28 UTC

    Here's another way to generate an 8 character string, containing characters in the ranges 2-9 and a-z, using the CPAN module String::Random:

    #!/usr/bin/perl -w use strict; use String::Random; my $string_random = String::Random->new; print $string_random->randregex('[a-z2-9]' x 8);
Re: Random Text: There's got to be an easier way
by John M. Dlugosz (Monsignor) on Jun 03, 2001 at 22:02 UTC
    I've done something similar for ID numbers. It is indeed a little shorter:

    my $length= 6; #how many digits to generate my @letters= split (//, '0123456789abcdefghijkmnopqrstuvwxyz'); sub create_new_ID { my $result; foreach my $iteration (1..$length) { $result .= $letters[int(rand(scalar @letters))]; } return $result; }
    I didn't use the dotdot in the list constructor, as you see. I think you could have written 'a'..'z' (that is, use the quotes) and not get the bareword warning. However, note that I leave out 'l' because it looks too much like a '1', and it should be somewhat human readable/reproducable, right?

    —John

      I didn't use the dotdot in the list constructor, as you see. I think you could have written 'a'..'z' (that is, use the quotes) and not get the bareword warning. However, note that I leave out 'l' because it looks too much like a '1', and it should be somewhat human readable/reproducable, right?
      human readable code is good too : my @letters= ("a".."k","m".."z","2".."9");
Re: Random Text: There's got to be an easier way
by shotgunefx (Parson) on Jun 03, 2001 at 22:23 UTC
    You may want to do a check for profanity. I know of one company who uses letters and numbers who's system generated at least a few orders with the word f*ck in it.

    This suprisingly enough offended some. ;)
    Actually excluding u will take care of most naughty words.

    -Lee

    "To be civilized is to deny one's nature."
      exclude u: nice idea. I don't like the idea of putting naughty words into the script for filtering purposes! A simple way of not generating them in the first place is elegant.

        seems to me that removing "u" won't actually solve the problem very well, but removing all vowels should do the trick. then again, if your users are particularly sensitive, they may be bothered by "fck" on its own.

Re: Random Text: There's got to be an easier way
by thpfft (Chaplain) on Jun 03, 2001 at 21:14 UTC

    $ENV{UNIQUE_ID} is usually available if you're on apache. It looks something like 'OxpuVdRDyd0AABcZKVw', so it isn't all that readable, but it's certainly an easier way.

    (nb this is no use at all if you need to create more than one id per script invocation)

    see the apache docs for more.

      $ENV{UNIQUE_ID} depends on the presence of mod_unique_id. I know I always include it in the build, but does everyone? :)

      Also note that Perl isn't always used as CGI. mod_unique_id is an Apache thing :)

      Greetz
      Beatnik
      ... Quidquid perl dictum sit, altum viditur.
Re: Random Text: There's got to be an easier way
by CharlesClarkson (Curate) on Jun 04, 2001 at 09:08 UTC
    my $rand; $rand .= ('a' .. 'z', 2 ..9)[rand 34] foreach 0 .. 7; print $rand;
    HTH,
    Charles K. Clarkson
Re: Random Text: There's got to be an easier way
by dvergin (Monsignor) on Jun 04, 2001 at 10:38 UTC
    Or generalize as a general-purpose routine so you don't have to write another one next time:
    sub make_rand_str { # e.g.: $str = make_rand_str(8, 'A'..'Z','2'..'9'); my ($num_chars, @possible_chr_array) = @_; my $num_possibles = @possible_chr_array; my $rnd_str = ""; for (1..$num_chars) { $rnd_str .= $possible_chr_array[rand($num_possibles)]; } return $rnd_str; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://85330]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (2)
As of 2024-04-19 19:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found