This Item isn’t really about random hex digits, but we thought we’d expand on an Item in the original Effective Perl blog that Joseph set up to support the first edition of Effective Perl Programming. He had an Item titled “Creating a String of Random Hex Digits”. We won’t reproduce it here, so you should read his version too.
Before you get too excited about creating the strings, think about your use of those strings. What do you really need, and is there a Perl module specifically designed for that task? Often, the string creation is only part of the task.
Create random strings
There are several ways to create a series of (pseudo)random characters, which is pretty easy to do as perl
one-liners.
You can use rand
to select from a list of characters:
$ perl -le 'print map { (q(a)..q(z))[rand(26)] } 1 .. 10' towfsqfpko
If you only wanted hex digits, as in Joseph’s original post, you can simply turn (pseudo)random numbers into their hexadecimal representation:
$ perl -le 'print map { sprintf q|%X|, rand(16) } 1 .. 10' 15C1629E27
You might also use one of the digesting methods, perhaps using only part of the string:
$ perl -MDigest::MD5=md5_hex -le 'print md5_hex(qq|@ARGV|)' foo acbd18db4cc2f85cedef654fccc4a4d8
$ perl -MDigest::SHA1=sha1_hex -le 'print sha1_hex(qq|@ARGV|)' foo 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
What do you really need?
Why do you need a series of (pseudo)random hex digits, though? Knowing how to do it isn’t all that interesting. What’s the real task? Consider some of the possibilities:
- Unique key for a database
- Temporary filename
- Starter password
Choosing a random string might not work for these. A random string isn’t necessarily unique. Even if you had true random strings, that doesn’t mean that the Universe won’t conspire against you to give you the same string twice in a row. Even things such as the MD5 and SHA1 digests rely on the size of their space to avoid collisions. No matter how unlikely, they don’t eliminate them.
If you want a unique key for your database, use your database server to make it for you (MySQL, PostgreSQL) using its internal features:
CREATE TABLE Cats ( id INT UNIQUE AUTO_INCREMENT, ... );
CREATE TABLE Cats ( id SERIAL, ... );
If you need a temporary filename, you need more than a filename—you need a filename that no one else is using. Not only that, you need to choose the name and then use it before someone else makes a file with the same name. There’s a race condition there. However, the File::Temp module handles it all for you, and it has been part of the Perl Standard Library since Perl 5.6.1:
$ perl -MFile::Temp=tempfile -le 'print +(tempfile)[1]' /var/folders/ww/wwVt8Ol0FNKXTcuuj9hWz++++TI/-Tmp-/oqVBixj_Sx
If you want a starter password, okay, some of these techniques might work. You can have that one. However, you might like to use String::Random, which can generate “random” strings based on patterns.
use 5.010; use String::Random qw(random_regex random_string); say random_regex( '\d\d\d\w\w\w[!@#$]\d' ); say random_regex( 'abc' . ('.' x 5) );
The output strings match the patterns that you setup:
0409qI@0 abc*x{Go
If you want to uniquely identify something even across computers and even networks, you might one of the UUID modules, such as Data::UUID. Universal Unique IDentifiers, or UUIDs, are designed to create these identifiers without coordination or knowledge of anything else generating similar identifiers. You don’t have to worry about any of the details yourself:
$ perl -MData::UUID -le 'my $ug = Data::UUID->new; print $ug->to_string( $ug->create )' 5612BC58-0D2A-11E0-A675-ABCE838F2980
Things to remember
- A random string is not the same as a unique string
- Figure out why you need a random string, possibly using a module that does that
- Use database features if you need a unique key