Max Merz
Max Merz

Reputation: 273

How to generate a random alphanumeric string with Erlang?

I'm trying to generate an random alphanumeric ID with Erlang. I naively tried crypto:strong_rand_bytes(Bytes) to generate a random binary and then used that binary like it was created with <<"my_unique_random_id">> - which didn't work because random bits are not necessarily a valid UTF-8 string, right?

Well, I looked for other options in the erlang docs and elsewhere, but I didn't find anything. Could someone point me to a solution?

Upvotes: 17

Views: 12021

Answers (6)

Heli Pilot
Heli Pilot

Reputation: 1

You may use function uef_bin:random_latin_binary/2 from here: https://github.com/DOBRO/uef-lib#uef_binrandom_latin_binary2

Bin = uef_bin:random_latin_binary(Length, any)

And then, if you need a string() type:

String = erlang:binary_to_list(Bin)

Upvotes: 0

dingo sky
dingo sky

Reputation: 1445

The problem with responses to the various "I need random strings" questions (in whatever language) is almost every solution uses a flawed specification, namely, string length. The questions themselves rarely reveal why the random strings are needed, but I will boldly assume they are to be used as identifiers which need to be unique.

There are two leading ways to get strictly unique strings: deterministically (which is not random) and store/compare (which is onerous). What to do? Give up the ghost. Go with probabilistic uniqueness instead. That is, accept that there is some (however small) risk that your strings won't be unique. This is where understanding collision probability and entropy are helpful.

So I'll rephrase my bold assumption as you need some number of identifiers with a small risk of repeat. As a concrete example, let's say you need 5 million Ids with a less than 1 in a trillion risk of repeat. So what length of string do you need? Well, that question is underspecified as it depends on the characters used. But more importantly, it's misguided. What you need is specification of the entropy of the strings, not their length.

This is where EntropyString can help.

Bits = entropy_string:bits(5.0e6, 1.0e12).
83.37013046707142
entropy_string:random_string(Bits).
<<"QDrjGQFGgGjJ4t9r2">>

There are other predefined characters sets, and you can specify your own characters as well (though for efficiency reasons only sets with powers of 2 characters are supported). And best of all, the risk of repeat in the specified number of strings is explicit. No more guessing with string length.

Upvotes: 2

cystbear
cystbear

Reputation: 716

I have prepared small module do to this
Also it uses crypto:rand_uniform/2 but not obsolete random:uniform

module(cloud_rnd).

-export([rnd_chars/1, rnd_numbers/1, rnd_chars_numbers/1]).

rnd_chars(L)         -> get_rnd(L, chars).
rnd_numbers(L)       -> get_rnd(L, numbers).
rnd_chars_numbers(L) -> get_rnd(L, chars_numbers).

get_rnd(L, chars)         -> gen_rnd(L, "abcdefghijklmnopqrstuvwxyz");
get_rnd(L, numbers)       -> gen_rnd(L, "1234567890");
get_rnd(L, chars_numbers) -> gen_rnd(L, "abcdefghijklmnopqrstuvwxyz1234567890").

gen_rnd(Length, AllowedChars) ->
  MaxLength = length(AllowedChars),
  lists:foldl(
    fun(_, Acc) -> [lists:nth(crypto:rand_uniform(1, MaxLength), AllowedChars)] ++ Acc end,
    [], lists:seq(1, Length)
  ).

Upvotes: 6

Vance Shipley
Vance Shipley

Reputation: 757

randchar(N) ->
   randchar(N, []).

randchar(0, Acc) ->
   Acc;
randchar(N, Acc) ->
   randchar(N - 1, [random:uniform(26) + 96 | Acc]).

Upvotes: 1

Tilman
Tilman

Reputation: 2005

It might depend on the randomness you need. Erlang's crypto module produces stronger random data than the random module (see also [erlang-questions] Yaws security alert - Yaws 1.93 and this question). If you want to use strong_rand_bytes to generate an ID maybe getting the base64 of it might be enough:

> base64:encode(crypto:strong_rand_bytes(Bytes)).

You could turn this into a list if needed.

Upvotes: 19

Bill the Lizard
Bill the Lizard

Reputation: 405975

According to Generating random strings in Erlang it only takes a few lines of Erlang to generate a string of a specified length from a certain set of characters.

get_random_string(Length, AllowedChars) ->
    lists:foldl(fun(_, Acc) ->
                        [lists:nth(random:uniform(length(AllowedChars)),
                                   AllowedChars)]
                            ++ Acc
                end, [], lists:seq(1, Length)).

The blog post has a line-by-line explanation of the code. Look to the comments for a couple of optimization tips.

Upvotes: 13

Related Questions