perl rand lc/uc

1.5k Views Asked by At

I'm trying to get random lower case/upper case in my 42 chars password. Somehow I instead get:

ucclcjuczlclucmlc0lcdlc5lc0ucdlccucmucquc5ucslc4lckucxuctlcvlcquclucpuc0ucvlczucducauczlcauc7lciucmlcplcjucolchucmucpuc9lcilcqlck

Here is my code:

@a = (0 .. 9, 'a' .. 'z');
@case = ('lc','uc'); 
my $x = join '', map { scalar $case[rand(0-2)],  $a[int rand @a]}  0 .. 42;
print "$x\n";

Any Hints?

7

There are 7 best solutions below

0
On

There are already several good answers, I will present two more, one that looks like how you were thinking, and the second using CPAN

First, in the manner of the OP: make anonymous subroutines which return the uc or lc of their argument, then randomly invoke one or the other on a random character.

#!/usr/bin/env perl

use strict;
use warnings;

my @a = (0 .. 9, 'a' .. 'z');
my @case = (sub{lc shift},sub{uc shift}); 
my $x = join '', map { $case[int rand(2)]->($a[int rand @a]) } 1 .. 42;
print "$x\n";

Ok, so now you have learned about function invoking. For your password generation, we take a quick look to CPAN and find String::Random. It lets you generate random strings based on patterns.

#!/usr/bin/env perl

use strict;
use warnings;

use String::Random 'random_regex';

print random_regex('[a-zA-Z]{42}'), "\n";

The last time I ran this I got

LpvrMpylaaZTzmMbyBLiNQiwFZyKWWesVecvBWbLwu
0
On

You can probably try something like the following one liner:

perl -le "print map { ('a'..'z', 'A'..'Z', '0'..'9')[rand 62] } 1..42"

SXQtgMmzL7hsF9EZ2p9DrGg8BudNh4QWkOQn6UurPk

perl -le "print map { ('a'..'z', 'A'..'Z', '0'..'9')[rand 62] } 1..42"

Rk6TJlBRkB3tS6fhmtXUNAmHSw04HNJTWCw2bhTcSl

For Linux you can replace " with ' and ' with ".

Source - catonmat.net

0
On

Why not just select a random alphanumeric character (that, in the case of letters, can be upper or lower case)?

use strict;
use warnings;
my @chars=("a".."z","A".."Z",0..9);
my $password="";

foreach(1..42)
{
  #tack on a random alphanumeric character
  $password.=$chars[int(rand(@chars))] 
}

print $password . "\n";

If you want to make lc or uc an explicit part of the process, you can do something like:

use strict;
use warnings;
my @chars=("a".."z",0..9);

my $password="";

foreach(1..42)
{
  my $char=$chars[int(rand(@chars))];
  $char=uc($char) if rand() <0.5;
  $password.=$char;
}

print $password . "\n";
1
On

Your code joins together the strings 'lc' or 'uc' with the random letters, rather than actually applying the lc or uc functions to the random letters. (You can't take a reference to built-in functions before Perl 5.16 anyway.)

0
On

The problem is that you have strings (uc or lc) that you expect to be executed as code, but they are never passed to the parser. You could pass them to the parser by using eval EXPR, but that's not the right approach.

my @chars = ('0'..'9', 'a'..'z');
my $passwd =
   join '',
    map rand >= 0.5 ? uc : lc,
     map $chars[rand(@chars)],
      1..42;

Or better yet:

my @chars = ('0'..'9', 'a'..'z', 'A'..'Z');
my $passwd =
   join '',
    map $chars[rand(@chars)],
     1..42;
0
On

No sense in using lcand uc when providing a list of upper and lower case is as simple as it is:

use strict;
use warnings;

sub random_pwd {
    my $length = shift;
    my @chars = (0 .. 9, 'a' .. 'z', 'A' .. 'Z');
    return join '', @chars[ map rand @chars, 0 .. $length ];
}
print random_pwd(42);

If you however wish to randomize caps in an already existing string, here's one way:

$str =~ s/(\w)/ rand() < 0.5 ? uc($1) : lc($1) /ge;

Somewhat stricter is using [a-z] instead of \w (which also includes numbers), but all you are losing is some redundant processing (e.g. uc("L") or lc("2")), which should not be a problem.

0
On

iSn't CPan wOndERFuL?

use Text::Capitalize;

print scramble_case "Isn't CPAN wonderful?";