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


180

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

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

Answers:


287

Mehrdad Afshari的答案可以解决问题,但我发现对于此简单任务而言,它太冗长了。查询表有时可以产生奇迹:

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

5
@Kent:不过,这是OpenSSL团队的工作,直到有人想到通过valgrind放置他们的代码。;-)
Konrad Rudolph

11
您可能不想使用带模数的简单rand()。参见: c-faq.com/lib/randrange.html
兰迪·普罗克

5
我认为这行s[len] = 0是不正确的。如果s是C字符串(以NULL结尾的字符串),则方法的签名将不必在其中包含len参数。Imo,如果您将长度作为参数传递,则假定数组不是C字符串。因此,如果不将C字符串传递给该函数,则该行s[len] = 0可能会中断事情,因为该数组将从0变为len-1。即使将C字符串传递给该函数,该行s[len] = 0也将是多余的。
费利佩2012年

16
请使用C ++ 11或随机增强,我们现在在2016年
Nikko

13
我们需要一种在stackoverflow上接收过时答案的方法。
Velkan '17

107

这是我使用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

  1. 因为您可以针对感兴趣的字符集生成遵循一定概率分布(或分布组合)的字符串。
  2. 因为它内置了对 非确定性随机数的支持
  3. 因为它支持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;
}

样本输出。


请注意,至少在MSVC 2012上,您将需要const auto randSeed = std :: random_device(),然后将randSeed传递给std :: default_random_engine()。std :: random_device {}()无法使用此版本进行编译。
NuSkooler 2015年

8
如果您使用的是C ++ 11,最好不要rand()在第一个代码段中使用它吗?
Ehtesh Choudhury 2015年

C ++ 11允许您将生成器与引擎分开,但是最好的选择取决于应用程序的需求。这就是为什么我的第一个代码段使用rand而第二个不使用rand的原因。
卡尔

7
我认为使用不再是正确的rand()。大声喊叫还不统一...
jeremyong

1
@Carl我认为在C ++社区中,除非均匀性外,由于许多原因,rand被弃用,并且是众所周知的反模式(请参阅STL的演讲“兰德认为有害”)。从视图中抽象生成器是一个通用的C ++概念,我认为这对于C ++的从业者和学生来说很重要(考虑它如何延续到std :: chrono,std :: string_view等)。
jeremyong

38

我的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;
}

也许用default_random_engine代替mt19937?该代码看起来更通用。
Velkan '17

1
@Velkan说实话,std::default_random_engine我对推荐这个建议并不满意,因为该标准不能保证其质量,效率或实现之间的可重复性。
Galik '17

1
为了避免使用char数组常量并因此必须使用sizeof,请将更改auto&std::string,这样可以使您std::string::length
smac89'1

我觉得访问a std::string可能会比较慢,因为它包含一个指向其数据的内部指针。那将意味着静态数组不需要额外的间接访问。也sizeof永远不会比std::string::size它慢,因为它是一个编译时间常数。
加里克

1
@Chronial是的,那会更好。但是std::size直到C++17那时才出现,仍然有很多人只在编码,C++11/14所以我现在将其保留。
Galik

14
 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 ---前主持人小猫,

2
@dmckee:是的,但是那些还有哪些其他字符集?(EBCDIC没有连续的字母)。
格雷格·休吉尔09年

1
嗯 我想我被抓了。我只是在
模仿

快速检查标准不会在我期望的2.2节中显示出这样的连续性要求。
David Thornley,2009年

2
但是,0..9必须是连续的。没有部门编号,但是我对此很确定。
Johannes Schaub-litb

10

我刚刚对此进行了测试,它可以很好地工作并且不需要查找表。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;
}

8
没有办法知道此功能将运行多长时间。这是不太可能的,但严格来说,这可以无限期地运行。
ctrlc-root

8

在这种情况下,最好使用适当的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_bits = sizeof(typename T::result_type) * T::state_size;
    auto constexpr seed_len = seed_bits / std::numeric_limits<std::seed_seq::result_type>::digits;
    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 <limits>
#include <random>
#include <string>

上面的代码使用类模板参数推导,因此需要C ++ 17。通过添加所需的模板参数,可以轻松地将其改编为早期版本。


是它只是演绎std::size_tstd::uniform_int_distribution?我看不到其他
CTAD

@Caleth正确。(为什么)这会让您感到惊讶?
康拉德·鲁道夫

我很想建议您采用rng默认参数,例如template <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth

我花了一点时间才看到它。我可能会在头脑上取代std::uniform_int_distribution<>,这很安全,但可能会警告有符号->无符号转换。
Caleth

6

我希望这可以帮助别人。

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

#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


3
加1,减1:读者,请当心:RandomString(100)!;-)
azhrei 2015年

2
这段代码仍然坏了,并且有几个问题。最重要的是,std::srand()应仅在程序开始时真正调用一次(最好是中的第一件事main())。如果在紧密循环中调用该代码,则将生成许多相同的“随机”字符串。
Galik

4

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

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

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

1
应该是randomDistribution(0,sizeof(allowed_chars)-2);
阿奇

@Archie为什么?看起来不错(minIndex,maxIndex)en.cppreference.com/w/cpp/numeric/random/…–
Oleg

1
因为allowed_chars []也包含“ \ 0”字符。
Archie


我更新了解决方案,std::string而不是使用它std::string::value_type[]
Oleg

1

如果您希望您的字符串包含任何可打印的字符,那么甚至更简单,更基本的东西:

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

1
我认为这是最简单的解决方案,并且绝对适用于具有指定字符集的情况
VolAnd '17

1

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

        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 LENGTH_NAME,但是没有。
Cornstalks '16

1

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

您能否详细说明答案?仅发布一段代码通常不是很有帮助。
Noel Widmer

1

让我们再次方便随意!

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

这是一个简短的说明,但是您可以点击链接查看完整的代码。解决方案的主要部分在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


0

由于没有答案,这又可以满足我的需要。首先,如果使用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;
}

-1

调用函数时要当心

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

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

-2
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';
}

看起来与排名靠前的答案大致相同。不确定此答案是否会增加任何价值。
jm。

是的,它确实会“ srand(time(NULL));”索引在每次迭代中都是随机的,使您的字符串更加随机xD每次他运行函数时,字符串都会有所不同...而且,Syms中的字符代表一个单个数组,而不是指向字符串的指针数组。
ДеянДобромиров

1
你试过了吗?srand(time(NULL))将整个周期内的随机数生成器重置为相同,因此它将基本上打印相同符号的行。
二OO Tiib

好工作,固定:)
ДеянДобромиров

它运行在我的STM32F4的xD
ДеянДобромиров
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.