views:

4914

answers:

6

I'd like to create a random string, consisting of alpha-numeric characters. I want to be able to be specify the length of the string.

How do I do this in C++?

+6  A: 
 void gen_random(char *s, const int len) {
     for (int i = 0; i < len; ++i) {
         int randomChar = rand()%(26+26+10);
         if (randomChar < 26)
             s[i] = 'a' + randomChar;
         else if (randomChar < 26+26)
             s[i] = 'A' + randomChar - 26;
         else
             s[i] = '0' + randomChar - 26 - 26;
     }
     s[len] = 0;
 }
Mehrdad Afshari
Nice: this is independent of character set (at least for all character sets that have a..z,A..Z, and 0..9 continguous).
dmckee
@dmckee: true, but what other character sets are those? (EBCDIC doesn't have contiguous letters).
Greg Hewgill
Um. I guess I'm caught out. I was just parroting something a professor said to me once...
dmckee
A quick check of the standard shows no such continuity requirements in section 2.2, where I'd expect them.
David Thornley
0..9 are required to be contiguous, though. no section number, but i'm sure about this.
Johannes Schaub - litb
read 22.2.1.1.2 para 13 .
Johannes Schaub - litb
A: 

Could you generate GUID's from the built in function(s) and then just trim off the length you need?

EJB
C++ (the language) doesn't have any built-in functions that generate GUIDs -- but there are libraries that do that.
Ates Goral
GUIDs contain only hexadecimal characters (0-9, A-F), while the original question asked for alphanumeric.
Greg Hewgill
I don't think GUID's are all that random. Some of the string is fixed per machine, right?
jm
+26  A: 

Mehrdad Afshari's answer would do the trick, but I found it a bit too verbose for this simple task. Look-up tables can sometimes do wonders:

void gen_random(char *s, const int len) {
    static const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";

    for (int i = 0; i < len; ++i) {
        s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
    }

    s[len] = 0;
}
Ates Goral
Nice. I wanted to mention this in a comment. This approach works better if, like this example, the count of elements is small. The drawback is it doesn't scale well if you wanted to generate random numbers from large distinct continues sets.
Mehrdad Afshari
I like this answer better. It's more flexible, because you can eliminate characters easily. For example, making a random alphanumeric string without the character I would be trivial in this function. It is also easier to understand, in my opinion.
William Brendel
+1, This way you can add certain special char without having to worry about the entire set. Much better.
WolfmanDragon
Mehrdad, why does it not scale? an array of char is an array of char wherever it is digits or alphabet. This is not creating random ints or floats but chars.
WolfmanDragon
I said it doesn't scale "if...". Clearly, this is a good solution for these situations.
Mehrdad Afshari
We had a bit of a language barrier here on this one. "Thus, a continuous set is a conditionally-complete lattice which is a dense total order. The phrase "continuous set" is not used in the Western literature." from http://eom.springer.de/c/c025760.htm
WolfmanDragon
WolframDragon, sorry, English is not my native language.
Mehrdad Afshari
Your answer is a good approach, but you should remove the "- 1" from your algorithm. It is incorrect. It is easy to see this if you visualize a smaller array size, such as 1 or 2.
Shmoopty
@Shmoopty - Thanks for the pointer. I already had my doubts when writing that but my VS was acting up and so I couldn't test my code :)
Ates Goral
Don't forget to call seed the random number generator :)
jm
Don't forget to use a High Entropy source for your seed either. ( Ie: not 0 + process ID, I hear a slice of uninitialised memory works nice )
Kent Fredric
this is a nice solution, +1 . but i think you want to index with "rand() % (sizeof(alphanum) - 1)", because the sizeof takes the terminating null character into account
Johannes Schaub - litb
ah, i just see you already had it in originally. well, it was right. let's see with an array of size 2: "1" ('1','\0'). (rand() % sizeof(alphanum)) would give you indices from 0..1 . but that is wrong, of course.
Johannes Schaub - litb
@litb - I knew it! I knew my gut feeling was right the first time. Reverted to use -1. Thanks!
Ates Goral
@Kent: that's what the OpenSSL team though, until someone thought of putting their code through valgrind. ;-)
Konrad Rudolph
You probably don't want to use a simple rand() with modulus. See:http://c-faq.com/lib/randrange.html
Randy Proctor
+3  A: 

I tend to always use the structured C++ ways for this kind of initialization. Notice that fundamentally, it's no different than Altan's solution. To a C++ programmer, it just expresses the intent a tad better and might be easier portable to other data types. In this instance, the C++ function generate_n expresses exactly what you want:

struct rnd_gen {
    rnd_gen(char const* range = "abcdefghijklmnopqrstuvwxyz0123456789")
        : range(range), len(std::strlen(range)) { }

    char operator ()() const {
        return range[static_cast<std::size_t>(std::rand() * (1.0 / (RAND_MAX + 1.0 )) * len)];
    }
private:
    char const* range;
    std::size_t len;
};

std::generate_n(s, len, rnd_gen());
s[len] = '\0';

By the way, read Julienne’s essay on why this calculation of the index is preferred over simpler methods (like taking the modulus).

Konrad Rudolph
+4  A: 

I just tested this, it works sweet and doesn't require a lookup table. rand_alnum() sort of forces out alphanumerics but because it selects 62 out of a possible 256 chars it isn't a big deal.

#include <cstdlib>   // for rand()
#include <cctype>    // for isalnum()   
#include <algorithm> // for back_inserter
#include <string>

char 
rand_alnum()
{
    char c;
    while (!std::isalnum(c = static_cast<char>(std::rand())))
        ;
    return c;
}


std::string 
rand_alnum_str (std::string::size_type sz)
{
    std::string s;
    s.reserve  (sz);
    generate_n (std::back_inserter(s), sz, rand_alnum);
    return s;
}