我想创建一个随机字符串,由字母-数字字符组成。我希望能够指定字符串的长度。

如何在c++中做到这一点?


当前回答

而不是手动循环,更喜欢使用适当的c++算法,在这种情况下std::generate_n,具有适当的随机数生成器:

auto generate_random_alphanumeric_string(std::size_t len) -> std::string {
    static constexpr auto chars =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    thread_local auto rng = random_generator<>();
    auto dist = std::uniform_int_distribution{{}, std::strlen(chars) - 1};
    auto result = std::string(len, '\0');
    std::generate_n(begin(result), len, [&]() { return chars[dist(rng)]; });
    return result;
}

这接近于我所说的这个问题的“规范”解决方案。

不幸的是,正确地播种一个通用的c++随机数生成器(例如MT19937)是非常困难的。因此上面的代码使用了一个辅助函数模板random_generator:

template <typename T = std::mt19937>
auto random_generator() -> T {
    auto constexpr seed_bytes = sizeof(typename T::result_type) * T::state_size;
    auto constexpr seed_len = seed_bytes / sizeof(std::seed_seq::result_type);
    auto seed = std::array<std::seed_seq::result_type, seed_len>();
    auto dev = std::random_device();
    std::generate_n(begin(seed), seed_len, std::ref(dev));
    auto seed_seq = std::seed_seq(begin(seed), end(seed));
    return T{seed_seq};
}

这很复杂,而且效率相对较低。幸运的是,它用于初始化thread_local变量,因此每个线程只调用一次。

最后,上述的必要包括:

#include <algorithm>
#include <array>
#include <cstring>
#include <functional>
#include <random>
#include <string>

上面的代码使用类模板参数演绎,因此需要c++ 17。通过添加所需的模板参数,可以对早期版本进行简单的修改。

其他回答

 void gen_random(char *s, size_t len) {
     for (size_t 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的回答可以解决这个问题,但我觉得对于这个简单的任务来说,它有点太啰嗦了。查询表有时可以创造奇迹:

#include <ctime>
#include <iostream>
#include <unistd.h>

std::string gen_random(const int len) {
    static const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    std::string tmp_s;
    tmp_s.reserve(len);

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

int main(int argc, char *argv[]) {
    srand((unsigned)time(NULL) * getpid());     
    std::cout << gen_random(12) << "\n";        
    return 0;
}

注意rand生成的随机数质量很差。

#include <iostream>
#include <string>
#include <random>

std::string generateRandomId(size_t length = 0)
{
    static const std::string allowed_chars {"123456789BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz"};

    static thread_local std::default_random_engine randomEngine(std::random_device{}());
    static thread_local std::uniform_int_distribution<int> randomDistribution(0, allowed_chars.size() - 1);

    std::string id(length ? length : 32, '\0');

    for (std::string::value_type& c : id) {
        c = allowed_chars[randomDistribution(randomEngine)];
    }

    return id;
}

int main()
{
    std::cout << generateRandomId() << std::endl;
}

这是另一种改编,因为没有一个答案能满足我的需求。

首先,如果rand()用于生成随机数,那么每次运行都将得到相同的输出。随机数生成器的种子必须是某种随机的。

在c++ 11中,你可以包含随机库,并且可以使用random_device和mt19937初始化种子。这个种子将由操作系统提供,它对我们来说足够随机(例如,时钟)。你可以给出一个范围边界(在我的例子中是[0,25])。

我只需要随机字符串小写字母,所以我利用字符加法。“人物池”的方法并不适合我。

#include <random>    
void gen_random(char *s, const int len){
    static std::random_device rd;
    static std::mt19937 mt(rd());
    static std::uniform_int_distribution<int> dist(0, 25);
    for (int i = 0; i < len; ++i) {
        s[i] = 'a' + dist(mt);
    }
    s[len] = 0;
}

我的2p解:

#include <random>
#include <string>

std::string random_string(std::string::size_type length)
{
    static auto& chrs = "0123456789"
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    thread_local static std::mt19937 rg{std::random_device{}()};
    thread_local static std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);

    std::string s;

    s.reserve(length);

    while(length--)
        s += chrs[pick(rg)];

    return s;
}