Como crio uma seqüência alfanumérica aleatória em C ++?

180

Eu gostaria de criar uma sequência aleatória, composta por caracteres alfanuméricos. Eu quero poder especificar o comprimento da string.

Como faço isso em C ++?

jm.
fonte

Respostas:

288

A resposta de Mehrdad Afshari seria suficiente, mas eu achei isso um pouco detalhado demais para essa tarefa simples. Às vezes, as tabelas de consulta podem fazer maravilhas:

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;
}
Ates Goral
fonte
5
@ Kent: é isso que a equipe do OpenSSL, até que alguém pensou em colocar seu código no valgrind. ;
Konrad Rudolph
11
Você provavelmente não deseja usar um rand () simples com módulo. Veja: c-faq.com/lib/randrange.html
Randy Proctor
5
Eu acho que a linha s[len] = 0está incorreta. Se sfor uma string C (terminada em NULL), a assinatura do método não precisaria conter o lenparâmetro. Imo, se você está passando o comprimento como argumento, está assumindo que a matriz não é uma string C. Portanto, se você não estiver passando uma string C para a função, a linha s[len] = 0poderá quebrar as coisas, pois a matriz passaria de 0 a len-1. E mesmo se você estiver passando uma string C para a função, a linha s[len] = 0seria redundante.
Felipe
16
Use C ++ 11 ou aumente aleatoriamente, estamos em 2016 agora #
Nikko
13
Precisamos de uma maneira de afundar respostas obsoletas no stackoverflow.
Velkan 23/05
107

Aqui está minha adaptação da resposta de Ates Goral usando C ++ 11. Eu adicionei o lambda aqui, mas o princípio é que você pode passá-lo e, assim, controlar quais caracteres sua string contém:

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

Aqui está um exemplo de passagem de um lambda para a função de sequência aleatória: http://ideone.com/Ya8EKf

Por que você usaria C ++ 11 ?

  1. Porque você pode produzir seqüências de caracteres que seguem uma certa distribuição de probabilidade (ou combinação de distribuição) para o conjunto de caracteres de seu interesse.
  2. Porque possui suporte embutido para números aleatórios não determinísticos
  3. Como ele suporta unicode, você pode alterar isso para uma versão internacionalizada.

Por exemplo:

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

Saída de amostra.

Carl
fonte
Observe que, pelo menos no MSVC 2012, você precisará const auto randSeed = std :: random_device () e passar randSeed para std :: default_random_engine (). std :: random_device {} () não pode compilar com esta versão.
NuSkooler
8
Se você estiver usando o C ++ 11, não é melhor não usar rand()no seu primeiro snippet de código?
Ehtesh Choudhury
O C ++ 11 permite que você separe o gerador do mecanismo, mas o melhor depende de quais são as necessidades do seu aplicativo. É por isso que meu primeiro snippet usa rand e o segundo não.
Carl
7
Eu diria que nunca é correto usar rand()mais. Não é nem uniforme para sair chorando ...
jeremyong
1
@Carl Acho que, na comunidade C ++, o rand está obsoleto e é um conhecido anti-padrão por muitas razões, além da não uniformidade (veja a palestra da STL "Rand Considered Harmful"). A abstração do gerador da visão é um conceito geral de C ++ que eu acho importante para os profissionais e estudantes de C ++ aprenderem (considere como ele é transferido para std :: chrono, std :: string_view, etc.).
jeremyong
39

Minha solução 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;
}
Galik
fonte
Talvez use em default_random_enginevez de mt19937? O código pareceria mais genérico.
Velkan 23/05
1
@Velkan Para ser honesto, std::default_random_enginenão é algo que eu me recomendo em recomendar, pois o padrão não garante sua qualidade, eficiência ou repetibilidade entre implementações.
Galik 23/05
1
Para evitar o uso de um literal matriz de char e, assim, ter que usar sizeof, mudar o auto&que std::string, o que lhe dástd::string::length
smac89
Achei que o acesso a std::stringprovavelmente era mais lento porque contém um ponteiro interno para seus dados. Isso significaria um indireção extra que uma matriz estática não requer. Também sizeofnunca pode ser mais lento do que std::string::sizeporque é uma constante de tempo de compilação.
Galik 28/03
1
@ Chronial Sim, de fato, isso seria melhor. No entanto std::size, não apareceu até C++17e ainda há muitas pessoas apenas codificando, C++11/14então vou deixar como está por enquanto.
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;
 }
Mehrdad Afshari
fonte
Agradável: isso é independente do conjunto de caracteres (pelo menos para todos os conjuntos de caracteres que têm a..z, A..Z e 0..9 contíguos).
dmckee --- ex-moderador gatinho
2
@ dmckee: verdade, mas que outros conjuntos de caracteres são esses? (EBCDIC não tem letras contíguas).
Greg Hewgill 13/01/09
1
Hum. Eu acho que estou preso. Eu estava apenas repetindo algo que um professor me disse uma vez ...
dmckee --- ex-moderador gatinho
Uma verificação rápida do padrão não mostra esses requisitos de continuidade na seção 2.2, onde eu os esperava.
18715 David Thornley
2
0..9 devem ser contíguos, no entanto. nenhum número de seção, mas tenho certeza disso.
Johannes Schaub - litb 14/01
10

Acabei de testar isso, funciona bem e não requer uma tabela de pesquisa. O rand_alnum () meio que expulsa os alfanuméricos, mas como seleciona 62 dos 256 caracteres possíveis, não é grande coisa.

#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;
}
apenas
fonte
8
Não há como saber quanto tempo essa função levará para executar. É muito improvável, mas estritamente falando, isso pode ocorrer indefinidamente.
Ctrlc-root
8

Em vez de fazer um loop manual, prefira usar o algoritmo C ++ apropriado , neste caso std::generate_n, com um gerador de números aleatórios apropriado :

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

Isso é próximo de algo que eu chamaria de solução "canônica" para esse problema.

Infelizmente, semear corretamente um gerador de números aleatórios C ++ genérico (por exemplo, MT19937) é realmente difícil . O código acima, portanto, usa um modelo de função auxiliar 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};
}

Isso é complexo e relativamente ineficiente. Felizmente, é usado para inicializar uma thread_localvariável e, portanto, é invocado apenas uma vez por thread.

Finalmente, as inclusões necessárias para o acima são:

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

O código acima usa dedução de argumento de modelo de classe e, portanto, requer C ++ 17. Ele pode ser adaptado trivialmente para versões anteriores adicionando os argumentos de modelo necessários.

Konrad Rudolph
fonte
é apenas dedução std::size_tpara std::uniform_int_distribution? Não consigo ver nenhum outro CTAD
Caleth
@Caleth Correct. (Por que) isso te surpreende?
Konrad Rudolph
Estou tentado a sugerir tomar rngcomo parâmetro padrão, com algo comotemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth
Levei um segundo para ver. Eu provavelmente estava substituindo mentalmente std::uniform_int_distribution<>, o que seria seguro, mas poderia avisar sobre conversão assinada -> não assinada.
Caleth 17/03
6

Espero que isso ajude alguém.

Testado em https://www.codechef.com/ide com 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

DD
fonte
3
Mais 1, Minus 1: Reader, cuidado: RandomString(100)! ;-)
azhrei
2
Este código ainda está quebrado e tem vários problemas. Mais importante ainda std::srand(), só deve ser chamado uma vez no início do programa (de preferência a primeira coisa a entrar main()). O código, como está, gerará muitas seqüências "aleatórias" idênticas se chamadas em um loop restrito.
Galik
4

Aqui está uma frase engraçada. Precisa de ASCII.

void gen_random(char *s, int l) {
    for (int c; c=rand()%62, *s++ = (c+"07="[(c+16)/26])*(l-->0););
}
geza
fonte
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;
}
Oleg
fonte
1
Deve ser randomDistribution (0, sizeof (allowed_chars) - 2);
22418 Archie
@Archie porque? Looks fina (minindex, maxindex) en.cppreference.com/w/cpp/numeric/random/...
Oleg
1
Porque allowed_chars [] também contém o caractere '\ 0'.
Archie
@Archie, você está certo wandbox.org/permlink/pxMJfSbhI4MxLGES Obrigado!
21418 Oleg
Eu atualizei a solução para usar std::stringem vez destd::string::value_type[]
Oleg
1

Algo ainda mais simples e mais básico, caso você esteja feliz por sua string conter caracteres imprimíveis:

#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
}
Nobilis
fonte
1
Acho que esta é a solução mais simples e absolutamente adequado para o caso do conjunto de caracteres especificado
Voland
1

Sequência aleatória, cada arquivo executado = sequência diferente

        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);
Nehigienix
fonte
Esse é um comportamento indefinido, porque std::generate_nassumirá o custom_stringcomprimento LENGTH_NAME, mas não o é.
Cornstalks
1

Exemplo para uso do 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;
}
Vitalja Alex
fonte
Você pode elaborar sua resposta? A publicação de apenas um pedaço de código geralmente não é muito útil.
quer
1

Vamos tornar o acaso conveniente novamente!

Eu criei uma boa solução apenas para cabeçalho C ++ 11. Você pode facilmente adicionar um arquivo de cabeçalho ao seu projeto e, em seguida, adicionar seus testes ou usar seqüências aleatórias para outros fins.

Essa é uma descrição rápida, mas você pode seguir o link para verificar o código completo. A principal parte da solução está na classe 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_);
    }
};

Randomerincorpora todas as coisas aleatórias e você pode adicionar suas próprias funcionalidades facilmente. Depois que tivermos Randomer, é muito fácil gerar strings:

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

Escreva suas sugestões para melhoria abaixo. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad

Gusev Slava
fonte
0

Mais uma adaptação, porque a falta de respostas bastaria para minhas necessidades. Antes de tudo, se rand () for usado para gerar números aleatórios, você obterá a mesma saída a cada execução. A semente do gerador de números aleatórios deve ser algum tipo de aleatória. Com o C ++ 11, você pode incluir a biblioteca "random" e inicializar a semente com random_device e mt19937. Essa semente será fornecida pelo sistema operacional e será aleatória o suficiente para nós (por exemplo, relógio). Você pode dar uma gama limites estão incluídos [0,25] no meu caso. E por último, mas não menos importante, eu só precisava de uma sequência aleatória de letras minúsculas, então utilizei a adição de caracteres. Com um conjunto de caracteres, a abordagem não funcionou para mim.

#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;
}
Barkin Kaplan
fonte
0
#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;
}
JK25
fonte
-1

Cuidado ao chamar a função

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

(adaptado de @Ates Goral ), sempre resultará na mesma sequência de caracteres. Usar

srand(time(NULL));

antes de chamar a função, embora a função rand () seja sempre propagada com 1 @kjfletch .

Por exemplo:

void SerialNumberGenerator() {

    srand(time(NULL));
    for (int i = 0; i < 5; i++) {
        cout << gen_random(10) << endl;
    }
}
Mr.Sheep
fonte
-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;
}
ras
fonte
-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';
}
Деян Добромиров
fonte
Parece exatamente o mesmo que a resposta mais bem classificada. Não tenho certeza se esta resposta agrega algum valor.
jm.
Sim, ele faz "srand (time (NULL));" O índice será aleatório em todas as iterações, tornando sua string mais aleatória xD A string será diferente toda vez que ele executar a função ... Além disso, os caracteres no Syms representam um matriz única, não matriz de ponteiros para seqüências de caracteres.
Деян Добромиров
1
Tentaste? srand (time (NULL)) redefine o gerador aleatório para o mesmo todo o ciclo e, portanto, basicamente imprime a linha do mesmo símbolo.
Öö Tiib 02/02
Bom trabalho lá, fixo :)
Деян Добромиров
Ele roda no meu STM32F4 xD
#: 43015