如何在 C++ 中创建随机字母数字字符串?

Posted

技术标签:

【中文标题】如何在 C++ 中创建随机字母数字字符串?【英文标题】:How do I create a random alpha-numeric string in C++? 【发布时间】:2010-10-01 04:58:06 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

标准库中最合适的函数是std::sample:

#include <algorithm>
#include <iterator>
#include <random>
#include <string>
#include <iostream>

static const char charset[] = 
    "0123456789"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz";

template<typename URBG>
std::string gen_string(std::size_t length, URBG&& g) 
    std::string result;
    result.resize(length);
    std::sample(std::cbegin(charset), 
                std::cend(charset),
                std::begin(result),
                std::intptr_t(length),
                std::forward<URBG>(g));
    return result;


int main() 
    std::mt19937 g;
    std::cout << gen_string(10, g) << std::endl;
    std::cout << gen_string(10, g) << std::endl;

随机数生成器的状态应保持在调用之间的函数之外。

【讨论】:

请注意,std::sample 的实现在标准中没有严格规定。尽管 g 生成的 PRNG 序列是相同的,但标准库的实现之间的结果可能会有所不同(实际上 libc++libstdc++ 的情况会有所不同。【参考方案2】:

Mehrdad Afshari 的 answer 可以解决问题,但我发现它对于这个简单的任务来说有点过于冗长。查找表有时可以创造奇迹:

#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;

注意randgenerates poor-quality random numbers。

【讨论】:

@Kent:这就是 OpenSSL 团队的做法,直到有人想到将他们的代码放到 valgrind 中。 ;-) 您可能不想使用带模数的简单 rand()。见:c-faq.com/lib/randrange.html 我认为s[len] = 0 这行不正确。如果s 是一个C 字符串(以NULL 结尾),那么方法的签名就不必在其中包含len 参数。 Imo,如果您将长度作为参数传递,则假设该数组不是 C 字符串。因此,如果您没有将 C 字符串传递给函数,s[len] = 0 行可能会破坏事情,因为数组将从 0 变为 len-1。即使您将 C 字符串传递给函数,s[len] = 0 行也是多余的。 请使用 C++11 或 boost random,我们现在是 2016 年 我们需要一种方法在 *** 上收集过时的答案。【参考方案3】:

又一次改编,因为没有一个答案能满足我的需要。

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

使用 C++11,您可以包含 random 库,并且可以使用 random_devicemt19937 初始化种子。这个种子将由操作系统提供,它对我们来说足够随机(例如,时钟)。您可以指定要包含的范围边界(在我的情况下为 [0,25]。

我只需要小写字母的随机字符串,所以我使用了char 加法。字符池方法不适合我。

#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;

【讨论】:

【参考方案4】:

您可以使用 random() 方法生成基本随机字符串。 下面的代码生成一个由小写字母、大写字母和数字组成的随机字符串。

String randomStrGen(int numChars)
  String genStr="";
  int sizeStr=0;
  
  while(sizeStr<numChars)
    int asciiPos= random(48,122);
    if((asciiPos>57 && asciiPos<65) || (asciiPos>90 && asciiPos<97))
      continue;
    genStr+=(char) asciiPos;
    sizeStr++;
  
  
  return genStr;

如果需要更安全的随机数生成器,只需将 random() 函数替换为更安全的。

此外,还可以通过将 ASCII 限制 (48,122) 更改为另一个自定义值来调整可能生成的字符

【讨论】:

对于那些想知道的人,random is POSIX 不是标准库的一部分【参考方案5】:

与其手动循环,不如使用适当的C++ algorithm,在本例中为std::generate_n,带有proper random number generator:

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;

这接近于我称之为该问题的“规范”解决方案。

很遗憾,correctly seeding a generic C++ random number generator (e.g. MT19937) is really hard。因此,上面的代码使用了一个辅助函数模板,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 Tseed_seq;

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

最后,上述必要的包括:

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

以上代码使用class template argument deduction,因此需要C++17。通过添加所需的模板参数,它可以很容易地适应早期版本。

【讨论】:

它只是为std::uniform_int_distribution 推导出std::size_t 吗?我看不到任何其他 CTAD @Caleth 正确。 (为什么)这让你吃惊吗? 我很想建议将rng 作为默认参数,例如template &lt;typename T = std::mt19937&gt; inline thread_local T default_rng = get_random_generator&lt;T&gt;(); 我花了一秒钟才看到它。我可能在精神上替换了std::uniform_int_distribution&lt;&gt;,这会很安全,但可能会警告有符号-> 无符号转换。 @user1032677 关键是std::random_device 只是在不提供更好熵源的平台上的 PRNG,所以你真的不能做得更好。我同意如果 std::random_device 不在缺乏良好熵源的平台上实现会更好,但我看不出这与我的答案有何具体关系。【参考方案6】:
//C++ Simple Code
#include <bits/stdc++.h>
using namespace std;
int main() 
vector<char> alphanum =
    '0','1','2','3','4',
'5','6','7','8','9',
'A','B','C','D','E','F',
'G','H','I','J','K',
'L','M','N','O','P',
'Q','R','S','T','U',
'V','W','X','Y','Z',
'a','b','c','d','e','f',
'g','h','i','j','k',
'l','m','n','o','p',
'q','r','s','t','u',
'v','w','x','y','z'
;
string s="";
int len=5;
srand(time(0)); 
for (int i = 0; i <len; i++) 
    int t=alphanum.size()-1;
    int idx=rand()%t;
    s+= alphanum[idx];

cout<<s<<" ";
return 0;

【讨论】:

见Why should I not #include <bits/stdc++.h>?。【参考方案7】:

我希望这对某人有所帮助。

在https://www.codechef.com/ide 使用 C++ 4.9.2 测试

#include <iostream>
#include <string>
#include <stdlib.h>     /* srand, rand */

using namespace std;

string RandomString(int len)

   string str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
   string newstr;
   int pos;
   while(newstr.size() != len) 
    pos = ((rand() % (str.size() - 1)));
    newstr += str.substr(pos,1);
   
   return newstr;


int main()

   srand(time(0));
   string random_str = RandomString(100);
   cout << "random_str : " << random_str << endl;

Output: random_str : DNAT1LAmbJYO0GvVo4LGqYpNcyK3eZ6t0IN3dYpHtRfwheSYipoZOf04gK7OwFIwXg2BHsSBMB84rceaTTCtBC0uZ8JWPdVxKXBd

【讨论】:

加 1,减 1:读者,当心:RandomString(100)! ;-) 这段代码仍然有问题并且有几个问题。最重要的是,std::srand() 应该只在程序开始时真正调用一次(最好是main() 中的第一件事)。如果在紧密循环中调用该代码,将生成许多相同的“随机”字符串。【参考方案8】:

Let's make random convenient again!

我编写了一个不错的 C++11 标头解决方案。 您可以轻松地将一个头文件添加到您的项目中,然后添加您的测试或将随机字符串用于其他目的。

这是一个简短的描述,但您可以点击链接查看完整代码。解决方案的主要部分在 Randomer 类中:

class Randomer 
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /* ... some convenience ctors ... */

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device())
        : gen_seed, dist_min, max 
    

    // if you want predictable numbers
    void SetSeed(unsigned int seed) 
        gen_.seed(seed);
    

    size_t operator()() 
        return dist_(gen_);
    
;

Randomer 封装了所有随机的东西,您可以轻松地添加自己的功能。有了Randomer之后,就很容易生成字符串了:

std::string GenerateString(size_t len) 
    std::string str;
    auto rand_char = []() return alphabet[randomer()]; ;
    std::generate_n(std::back_inserter(str), len, rand_char);
    return str;

在下面写下您的改进建议。 https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad

【讨论】:

【参考方案9】:
#include <iostream>
#include <string>
#include <stdlib.h>
int main()

    int size;
    std::cout << "Enter size : ";
    std::cin >> size;
    std::string str;
    for (int i = 0; i < size; i++)
    
        auto d = rand() % 26 + 'a';
        str.push_back(d);
    
    for (int i = 0; i < size; i++)
    
        std::cout << str[i] << '\t';
    

    return 0;

【讨论】:

【参考方案10】:
 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;
 

【讨论】:

很好:这与字符集无关(至少对于所有具有 a..z、A..Z 和 0..9 连续的字符集)。 @dmckee: 是的,但还有哪些其他字符集? (EBCDIC 没有连续的字母)。 嗯。我想我被抓住了。我只是在重复一位教授曾经对我说过的话...... 对标准的快速检查显示第 2.2 节中没有这样的连续性要求,我希望它们在那里。 0..9 必须是连续的。没有节号,但我确定这一点。【参考方案11】:
#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;

【讨论】:

应该是 randomDistribution(0, sizeof(allowed_chars) - 2); @Archie 为什么?看起来不错(minIndex,maxIndex)en.cppreference.com/w/cpp/numeric/random/… 因为 allowed_chars[] 还包含 '\0' 字符。 @Archie 你是对的wandbox.org/permlink/pxMJfSbhI4MxLGES 谢谢! 我将解决方案更新为使用std::string 而不是std::string::value_type[]【参考方案12】:

Qt 使用示例:)

QString random_string(int length=32, QString allow_symbols=QString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")) 
    QString result;
    qsrand(QTime::currentTime().msec());
    for (int i = 0; i < length; ++i)             
        result.append(allow_symbols.at(qrand() % (allow_symbols.length())));
    
    return result;

【讨论】:

你能详细说明你的答案吗?只发布一段代码通常不是很有帮助。【参考方案13】:

这是一个有趣的单线。需要 ASCII。

void gen_random(char *s, int l) 
    for (int c; c=rand()%62, *s++ = (c+"07="[(c+16)/26])*(l-->0););

【讨论】:

【参考方案14】:

我的 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 rgstd::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;

【讨论】:

@Velkan 老实说std::default_random_engine 不是我推荐的好东西,因为该标准不保证其质量、效率或实现之间的可重复性。 为避免使用 char 数组字面量并因此不得不使用 sizeof,请将 auto&amp; 更改为 std::string,这样您就可以得到 std::string::length @Chronial 确实,那样会更好。但是std::size 直到C++17 才出现,而且仍然有很多人只对C++11/14 进行编码,所以我暂时保留它。 不应该sizeof(chrs) - 2sizeof(chrs) - 1,因为uniform_int_distribution 包括最终值(这是一个包含限制)? @RichardWhitehead 不,因为字符数组 chars 包含空终止符 - 将 1 添加到其长度。这意味着有sizeof(chrs) - 1 有效字符,并且数组索引从0 开始,因此包含范围将是[0, sizeof(chrs) - 2],因为sizeof(chrs) - 2 是最后一个有效字符的索引。【参考方案15】:

随机字符串,每个运行文件=不同的字符串

        auto randchar = []() -> char
    
        const char charset[] =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz";

        const size_t max_index = (sizeof(charset) - 1);

        return charset[randomGenerator(0, max_index)];
    ;
            std::string custom_string;
            size_t LENGTH_NAME = 6 // length of name
    generate_n(custom_string.begin(), LENGTH_NAME, randchar);

【讨论】:

这是未定义的行为,因为std::generate_n 将假定custom_string 的长度为LENGTH_NAME,但事实并非如此。【参考方案16】:

调用函数时要小心

string gen_random(const int len) 
static const char alphanum[] = "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

stringstream ss;

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

return ss.str();

(改编自@Ates Goral)每次都会产生相同的字符序列。使用

srand(time(NULL));

在调用函数之前,虽然 rand() 函数总是以 1 @kjfletch 为种子。

例如:

void SerialNumberGenerator() 

    srand(time(NULL));
    for (int i = 0; i < 5; i++) 
        cout << gen_random(10) << endl;
    

【讨论】:

【参考方案17】:
void strGetRandomAlphaNum(char *sStr, unsigned int iLen)

  char Syms[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  unsigned int Ind = 0;
  srand(time(NULL) + rand());
  while(Ind < iLen)
  
    sStr[Ind++] = Syms[rand()%62];
  
  sStr[iLen] = '\0';

【讨论】:

看起来与排名靠前的答案几乎相同。不确定这个答案是否会增加任何价值。 是的 " srand(time(NULL)); " 索引在每次迭代中都是随机的,使你的字符串更加随机 xD 每次运行函数时字符串都会不同... Syms 中的字符也表示单个数组,而不是指向字符串的指针数组。 你试过了吗? srand(time(NULL)) 将随机生成器重置为相同的所有周期,因此它基本上会打印相同符号的行。 干得好,已修复:) 它在我的 STM32F4 xD 上运行【参考方案18】:

这是我使用 C++11 改编的 Ates Goral 的答案。我在这里添加了 lambda,但原则是您可以将其传入,从而控制您的字符串包含哪些字符:

std::string random_string( size_t length )

    auto randchar = []() -> char
    
        const char charset[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
        const size_t max_index = (sizeof(charset) - 1);
        return charset[ rand() % max_index ];
    ;
    std::string str(length,0);
    std::generate_n( str.begin(), length, randchar );
    return str;

这是一个将 lambda 传递给随机字符串函数的示例:http://ideone.com/Ya8EKf

为什么要使用 C++11

    因为您可以为您感兴趣的字符集生成遵循特定概率分布(或分布组合)的字符串。 因为它内置了对非确定性随机数的支持 因为它支持 unicode,所以您可以将其更改为国际化版本。

例如:

#include <iostream>
#include <vector>
#include <random>
#include <functional> //for std::function
#include <algorithm>  //for std::generate_n

typedef std::vector<char> char_array;

char_array charset()

    //Change this to suit
    return char_array( 
    '0','1','2','3','4',
    '5','6','7','8','9',
    'A','B','C','D','E','F',
    'G','H','I','J','K',
    'L','M','N','O','P',
    'Q','R','S','T','U',
    'V','W','X','Y','Z',
    'a','b','c','d','e','f',
    'g','h','i','j','k',
    'l','m','n','o','p',
    'q','r','s','t','u',
    'v','w','x','y','z'
    );
;    

// given a function that generates a random character,
// return a string of the requested length
std::string random_string( size_t length, std::function<char(void)> rand_char )

    std::string str(length,0);
    std::generate_n( str.begin(), length, rand_char );
    return str;


int main()

    //0) create the character set.
    //   yes, you can use an array here, 
    //   but a function is cleaner and more flexible
    const auto ch_set = charset();

    //1) create a non-deterministic random number generator      
    std::default_random_engine rng(std::random_device());

    //2) create a random number "shaper" that will give
    //   us uniformly distributed indices into the character set
    std::uniform_int_distribution<> dist(0, ch_set.size()-1);

    //3) create a function that ties them together, to get:
    //   a non-deterministic uniform distribution from the 
    //   character set of your choice.
    auto randchar = [ ch_set,&dist,&rng ]()return ch_set[ dist(rng) ];;

    //4) set the length of the string you want and profit!        
    auto length = 5;
    std::cout<<random_string(length,randchar)<<std::endl;
    return 0;

Sample output.

【讨论】:

请注意,至少在 MSVC 2012 上,您需要 const auto randSeed = std::random_device(),然后将 randSeed 传递给 std::default_random_engine()。 std::random_device() 无法使用此版本进行编译。 如果您使用的是 C++11,在您的第一个代码 sn-p 中不使用rand() 不是更好吗? C++11 允许您将生成器与引擎分开,但最好的方法取决于您的应用程序的需求。这就是为什么我的第一个 sn-p 使用 rand 而第二个没有。 我认为不再使用rand()永远正确的。大声喊叫连制服都不行…… @Carl 我认为在 C++ 社区中,rand 已被弃用,并且是众所周知的反模式,除了不均匀性之外还有很多原因(请参阅 STL 的演讲“Rand 被认为是有害的”)。从视图中对生成器的抽象是一个通用的 C++ 概念,我认为对于 C++ 的从业者和学生来说学习很重要(考虑它如何继承到 std::chrono、std::string_view 等)。【参考方案19】:

如果你对你的字符串包含任何可打印的字符感到高兴,那么一些更简单和更基本的东西:

#include <time.h>   // we'll use time for the seed
#include <string.h> // this is for strcpy

void randomString(int size, char* output) // pass the destination size and the destination itself

    srand(time(NULL)); // seed with time

    char src[size];
    size = rand() % size; // this randomises the size (optional)

    src[size] = '\0'; // start with the end of the string...

    // ...and work your way backwards
    while(--size > -1)
        src[size] = (rand() % 94) + 32; // generate a string ranging from the space character to ~ (tilde)

    strcpy(output, src); // store the random string

【讨论】:

我想这是最简单的解决方案,绝对适合指定字符集的情况 如果你根据时间在函数内部做种子,如果在循环中调用,你可以很容易地得到一堆相同的随机数。更好的方法是在每个进程运行时播种一次,但是如果您在紧密循环中多次运行该进程,则会出现同样的问题。其他答案更好,即使您真的不关心 prng 质量并且不介意使用rand【参考方案20】:

我刚刚对此进行了测试,效果很好,并且不需要查找表。 rand_alnum() 有点强制排除字母数字,但因为它从可能的 256 个字符中选择 62 个,所以没什么大不了的。

#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;

【讨论】:

没有办法知道这个函数需要多长时间才能运行。这不太可能,但严格来说,这可能会无限期地运行。

以上是关于如何在 C++ 中创建随机字母数字字符串?的主要内容,如果未能解决你的问题,请参考以下文章

在Java中创建一个带有A-Z和0-9的随机字符串[重复]

swift 在swift中创建所需长度的随机字母数字字符串

c++ 生成随机字符串转

如何在Javascript中创建随机字符串? [复制]

在字符串中创建所有字母替换组合

如何在 C++ 中创建字符串