C++ random string generator

Generate one here, or copy the C++ below. Every example was compiled with GCC 16 on MinGW-w64 and run.

G
More options
up to 10,000

Constraints

The short answer

Draw from std::random_device through std::uniform_int_distribution, and use the result as an index into your alphabet. The distribution performs the rejection sampling, so every character is equally likely, and on libstdc++, libc++ and the Microsoft standard library random_device reads the operating system random source.

The standard does not guarantee that last part. It allows random_device to be a deterministic generator where no hardware or system source is available, and MinGW builds before GCC 9.2 were exactly that, returning the same sequence on every run. Where a guarantee matters, call the operating system directly: BCryptGenRandom on Windows, getrandom on Linux, arc4random_buf on the BSDs and macOS, or randombytes_uniform from libsodium on any of them.

Do not test random_device with its entropy() member. With GCC 16 on MinGW-w64 it returned 0 while the device was producing different values on every call, so the number says nothing useful about the output.

C++ sources of randomness
SourceWhat it isSafe for secrets
std::rand()Implementation-defined; RAND_MAX was 32767 with MinGW-w64No
std::mt19937Mersenne Twister, 624 words of stateNo
std::mt19937 seeded with rd()Still Mersenne Twister, from a 32-bit seedNo
std::random_deviceThe system source on major standard librariesUsually
BCryptGenRandom, getrandom, arc4random_bufThe system CSPRNG, called directlyYes
libsodium randombytes_uniformThe system CSPRNG, unbiased by designYes

Why rand and mt19937 fail

std::rand() is allowed to be weak and usually is. With RAND_MAX at 32767 each call yields at most 15 bits, and rand() % 62 favours the first 32 characters of the alphabet slightly, because 32,768 leaves a remainder of 32 when divided by 62. Seeding with srand(time(nullptr)) means anyone who knows roughly when the program ran can try every candidate seed.

std::mt19937 is a much better generator for simulations and a poor one for secrets. A default-constructed mt19937 always starts from the seed 5489, so it produced the identical first value on every run when checked for this page. Seeding it with a single call to random_device gives an unpredictable start, but only one of about four billion starting points, and the internal state can be reconstructed from 624 consecutive outputs.

Neither problem is visible in the output. A string from mt19937 looks exactly as random as a string from the operating system, which is why the mistake survives.

Mistakes that pass code review

The first three patterns below compiled without a single warning under -Wall with GCC 16, and the sizeof version picked the NUL character 9,793 times in 630,000 draws.

  • const char alphabet[] = "..."; followed by a distribution over 0 to sizeof(alphabet) - 1. sizeof counts the terminating null character, so the string occasionally contains a NUL byte that truncates it wherever it is treated as a C string. Use sizeof(alphabet) - 2 as the upper bound, or a std::string.
  • alphabet[rd() % alphabet.size()]. The source is fine and the bias is tiny, about one part in 69 million for 62 characters over a 32-bit draw, but uniform_int_distribution removes it for free.
  • std::shuffle over the alphabet and taking the first n characters. No character can repeat, so 24 characters from 62 carry 135.5 bits instead of 142.9, and the length is capped at the alphabet size.
  • Relying on a fixed seed for reproducible test data and later reusing the same function for tokens. Keep the two generators in separate functions so the seeded one cannot leak into production code.
  • Expecting uniform_int_distribution to give the same sequence on every compiler for a given engine and seed. The algorithm is implementation-defined, so libstdc++, libc++ and the Microsoft library can differ.

Many unique strings

Insert strings into a std::unordered_set<std::string> until it reaches the size you need. At 16 characters from 62 a collision is so unlikely that the loop almost never repeats, and the set makes uniqueness certain within the process.

Doing this in your own code

C++11 and later - random_device
#include <iostream>
#include <random>
#include <string>

std::string random_string(std::size_t length) {
    static const std::string alphabet =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    // uniform_int_distribution does the rejection sampling, so there is no
    // modulo bias to remove by hand.
    std::random_device rd;
    std::uniform_int_distribution<std::size_t> pick(0, alphabet.size() - 1);

    std::string out;
    out.reserve(length);
    for (std::size_t i = 0; i < length; ++i) out += alphabet[pick(rd)];
    return out;
}

int main() {
    for (int i = 0; i < 3; ++i) std::cout << random_string(24) << '\n';
}

random_device reads the system source on libstdc++, libc++ and MSVC, but the standard allows a deterministic fallback. MinGW builds before GCC 9.2 had one.

C++ on Windows - BCryptGenRandom
#include <windows.h>
#include <bcrypt.h>
#include <iostream>
#include <stdexcept>
#include <string>

// Link with bcrypt.lib on MSVC, or -lbcrypt on MinGW.
std::string random_string(std::size_t length) {
    static const std::string alphabet =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    const unsigned n = static_cast<unsigned>(alphabet.size());
    const unsigned limit = 256 - (256 % n);  // largest multiple of n below 256

    std::string out;
    unsigned char buf[64];
    while (out.size() < length) {
        if (!BCRYPT_SUCCESS(BCryptGenRandom(nullptr, buf, sizeof buf, BCRYPT_USE_SYSTEM_PREFERRED_RNG)))
            throw std::runtime_error("BCryptGenRandom failed");
        for (unsigned char b : buf) {
            if (b < limit) out += alphabet[b % n];
            if (out.size() == length) break;
        }
    }
    return out;
}

int main() {
    for (int i = 0; i < 3; ++i) std::cout << random_string(24) << '\n';
}

The byte-level rejection assumes an alphabet of at most 256 characters.

C++11 and later - hex token
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include <string>

std::string hex_token(std::size_t bytes) {
    std::random_device rd;
    std::uniform_int_distribution<int> byte(0, 255);
    std::ostringstream out;
    for (std::size_t i = 0; i < bytes; ++i)
        out << std::hex << std::setw(2) << std::setfill('0') << byte(rd);
    return out.str();
}

int main() {
    std::cout << hex_token(32) << '\n';  // 64 hex characters, 256 bits
    std::cout << hex_token(16) << '\n';  // 32 hex characters, 128 bits
}
C++11 and later - password with every character type
#include <algorithm>
#include <array>
#include <iostream>
#include <random>
#include <stdexcept>
#include <string>

std::string password(std::size_t length) {
    static const std::array<std::string, 4> classes = {
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz", "0123456789", "!#$%*+-=?@^_"};
    if (length < classes.size()) throw std::invalid_argument("length is shorter than the number of classes");
    std::string alphabet;
    for (const auto& c : classes) alphabet += c;

    std::random_device rd;
    std::uniform_int_distribution<std::size_t> pick(0, alphabet.size() - 1);
    for (;;) {
        std::string s;
        for (std::size_t i = 0; i < length; ++i) s += alphabet[pick(rd)];
        // Whole-string rejection keeps the result uniform over valid passwords.
        bool valid = std::all_of(classes.begin(), classes.end(), [&](const std::string& c) {
            return s.find_first_of(c) != std::string::npos;
        });
        if (valid) return s;
    }
}

int main() {
    for (int i = 0; i < 5; ++i) std::cout << password(20) << '\n';
}

Questions

How do I generate a random string in C++?

Create a std::random_device and a std::uniform_int_distribution<std::size_t> over 0 to alphabet.size() - 1, then append alphabet[dist(rd)] once per character. The distribution is unbiased, and random_device reads the system source on the major standard libraries.

Is std::random_device cryptographically secure?

On libstdc++, libc++ and the Microsoft standard library it reads the operating system random source, which is. The C++ standard does not require that, and MinGW builds before GCC 9.2 returned a fixed sequence. For a hard guarantee, call BCryptGenRandom, getrandom or arc4random_buf, or use libsodium.

Can I use std::mt19937 to generate passwords?

No. Mersenne Twister is fast and statistically good, but not cryptographic: its state can be reconstructed from 624 consecutive outputs, a default-constructed mt19937 always starts from seed 5489, and seeding it from random_device still limits it to about four billion starting points.

Why does my random string sometimes come out shorter?

Usually because the distribution upper bound was computed with sizeof on a char array, which includes the terminating null character. When the NUL is chosen, anything that treats the result as a C string stops there. Subtract 2 from sizeof for the upper bound, or use std::string::size() minus 1.

Related generators