1

I have a tool that needs to create a unique directory each time it is launched.

This tool can be launched multiple times at the same second

When I use the regular srand(time(NULL)) and rand(), I get the same "unique" string if my tool was launched more than once at the same second.

Here is an example for my problem:

$ ./rand_string.exe
9IQMT8F63P37G3LO4O85LM1LRTEBTV3Q1DOX3B46SAUYUBPYKDMZER9M8DAZSVPT

$ ./rand_string.exe
I01QZ47I0FV3WCW597NLE3M9B75Q5S5ADFB23T4OZR0W2VM2E91XJYHWGGVMEAE0

# Time is still the same, getting same "unique" string
$ ./rand_string.exe
I01QZ47I0FV3WCW597NLE3M9B75Q5S5ADFB23T4OZR0W2VM2E91XJYHWGGVMEAE0

# Time is still the same, getting same "unique" string
$ ./rand_string.exe
I01QZ47I0FV3WCW597NLE3M9B75Q5S5ADFB23T4OZR0W2VM2E91XJYHWGGVMEAE0

$ ./rand_string.exe
LWNXWSTODGGC8B46JB0ZII950LJOPJ8EG3GEI885U58CLXHA3L0DBCXIX6I0I2SZ

$ ./rand_string.exe
UUY1245J9RO8O1G6OVEYBZUTK0PNGG9ER52JIQSN1MEUDGZEXHFBHJ6R6TJ74H1Q

$ ./rand_string.exe
3SP49GYF6HG4KAS7UVTELWWG4FW28UAY384PI8CLP3ZS50WIRXFTMQEKEWKE6DQY

# Time is still the same, getting same "unique" string
$ ./rand_string.exe
3SP49GYF6HG4KAS7UVTELWWG4FW28UAY384PI8CLP3ZS50WIRXFTMQEKEWKE6DQY

Here is my code (just to make sense for the "random" implementation):

#include <cstdlib>
#include <ctime>
#include <iostream>

void cpy_rand_str(char *dest, int size) {
        int i;
        int rand_num;

        srand (time(NULL));

        for (i = 0 ; i < size ; i++) {
                rand_num = rand() % 36;
                if (rand_num >= 10) {
                        rand_num += ('A' - 10);
                } else {
                        rand_num += '0';
                }
                dest[i] = (char)rand_num;
        }
        dest[size] = '\0';
}


int main() {
        char my_key[64 + 1];

        cpy_rand_str(my_key, 64);
        std::cout << my_key << "\n";

        return 0;
}
JBentley
  • 6,099
  • 5
  • 37
  • 72
SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
  • Becuase that's how `srand` works. Calling it will set the seed for the random numbers generated. And the seed depends on the time, to the second. So calling it several times per second will result in the same "random" string. – AntonH Jul 10 '14 at 04:59
  • 5
    Mix in something unique like the PID, or seed with a higher resolution counter. – Retired Ninja Jul 10 '14 at 05:00
  • @AntonH, I know, hence I ask if someone knows another solution – SomethingSomething Jul 10 '14 at 05:00
  • @user3322273 Sorry. Since I didn't see a question per se, I thought you wanted explaination, not alternative. – AntonH Jul 10 '14 at 05:02
  • @RetiredNinja, your solution sounds great to me. It could be nice if you write it as an answer, so it can be more bold also for other people that would watch this thread – SomethingSomething Jul 10 '14 at 05:02
  • 2
    @user3322273 If C++11 is allowed, you could look into a solution in the `` header as well. – IllusiveBrian Jul 10 '14 at 05:05
  • I would even go further and additionally add more than one random unique number. I mean worst case you might get two times the same PID at the same second. Add something like an additional rand, just to be sure. – Theolodis Jul 10 '14 at 05:05
  • 1
    @Theolodis, which additional rand? If I rand it with `srand()` I'd get each time the same "additional rand" :) – SomethingSomething Jul 10 '14 at 05:19
  • 1
    As you can see, `srand` is only suitable for throwaway toys; not for any serious application – M.M Jul 10 '14 at 05:44
  • Do you need the random name to be well random in cryptographic quality of random data sense, or just to be unique? The first version is difficult and system dependent, making it simply unique is different (and you can think of solutions without randomness) – Jakub Jul 10 '14 at 05:46

2 Answers2

2

This is pretty much what tmpnam was designed to accomplish. It's not perfect (e.g., it's open to race conditions) but stands at least some chance of working, which is more than you can say for most of the obvious alternatives. As a bonus, the string it generates is specifically intended to conform to the host's requirements as a file name, which usually means it's usable as the name of a directory.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 2
    The link you provided says under bugs "Never use this function. Use mkstemp(3) or tmpfile(3) instead." Those two return `FILE*` and an `int` of the file descriptor respectively, so they are hopefully usable for the OP. – IllusiveBrian Jul 10 '14 at 05:21
  • 1
    [mkdtemp](http://linux.die.net/man/3/mkdtemp) might be a better choice if it is available. – Retired Ninja Jul 10 '14 at 05:23
  • @Namfuak: Those both create *files*, not directories as he requested. `mkdtemp` should work, but I hesitate to recommend it simply be availability is fairly limited. Shortcomings of `tmpnam` are somewhat overblown--it can be a problem if you use it poorly, but with reasonable care, most such problems can be avoided. OTOH, if you're depending on it for security, that could be a real problem. – Jerry Coffin Jul 10 '14 at 05:32
  • Looks like `mkdtemp()` is the correct solution for my case – SomethingSomething Jul 10 '14 at 05:53
2

You could use actual UUIDs with boost for example:

boost::uuids::basic_random_generator<boost::mt19937> gen;
boost::uuids::uuid u = gen(); //generate uuid

std::string s1 = to_string(u);

Which will be platform independent and have a very low probability for collision.

Scis
  • 2,934
  • 3
  • 23
  • 37