Comment créer une chaîne alphanumérique aléatoire en C ++?


180

Je voudrais créer une chaîne aléatoire, composée de caractères alphanumériques. Je veux pouvoir spécifier la longueur de la chaîne.

Comment faire cela en C ++?

Réponses:


288

La réponse de Mehrdad Afshari ferait l'affaire, mais je l'ai trouvée un peu trop verbeuse pour cette tâche simple. Les tables de consultation peuvent parfois faire des merveilles:

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: c'est ce que l'équipe OpenSSL cependant, jusqu'à ce que quelqu'un pense à mettre son code via valgrind. ;-)
Konrad Rudolph

11
Vous ne voulez probablement pas utiliser un simple rand () avec module. Voir: c-faq.com/lib/randrange.html
Randy Proctor

5
Je pense que la ligne s[len] = 0est incorrecte. Si sest une chaîne C (terminée par NULL), alors la signature de la méthode n'aurait pas à contenir le lenparamètre. Imo, si vous passez la longueur comme argument, vous supposez que le tableau n'est pas une chaîne C. Ainsi, si vous ne passez pas de chaîne C à la fonction, la ligne s[len] = 0pourrait casser des choses, puisque le tableau passerait de 0 à len-1. Et même si vous passez une chaîne C à la fonction, la ligne s[len] = 0serait redondante.
Felipe

16
Veuillez utiliser C ++ 11 ou booster aléatoire, nous sommes en 2016 maintenant
Nikko

13
Nous avons besoin d'un moyen d'absorber des réponses obsolètes sur stackoverflow.
Velkan

107

Voici mon adaptation de la réponse d'Ates Goral en utilisant C ++ 11. J'ai ajouté le lambda ici, mais le principe est que vous pouvez le transmettre et ainsi contrôler les caractères que contient votre chaîne:

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

Voici un exemple de passage d'un lambda à la fonction de chaîne aléatoire: http://ideone.com/Ya8EKf

Pourquoi utiliseriez-vous C ++ 11 ?

  1. Parce que vous pouvez produire des chaînes qui suivent une certaine distribution de probabilité (ou combinaison de distribution) pour le jeu de caractères qui vous intéresse.
  2. Parce qu'il a un support intégré pour les nombres aléatoires non déterministes
  3. Parce qu'il prend en charge l'unicode, vous pouvez donc le changer en une version internationalisée.

Par exemple:

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

Exemple de sortie.


Notez qu'au moins sur MSVC 2012, vous devrez configurer auto randSeed = std :: random_device (), puis passer randSeed à std :: default_random_engine (). std :: random_device {} () ne peut pas compiler avec cette version.
NuSkooler

8
Si vous utilisez C ++ 11, n'est-il pas préférable de ne pas l'utiliser rand()dans votre premier extrait de code?
Ehtesh Choudhury

C ++ 11 vous permet de séparer le générateur du moteur, mais ce qui est le mieux dépend des besoins de votre application. C'est pourquoi mon premier extrait de code utilise rand et le second non.
Carl

7
Je dirais qu'il n'est plus correct de l'utiliser rand(). Ce n'est même pas uniforme pour pleurer à haute voix ...
jeremyong

1
@Carl Je pense que dans la communauté C ++, rand est obsolète et est un anti-pattern bien connu pour de nombreuses raisons en dehors de la non-uniformité (voir le discours de STL "Rand Considered Harmful"). L'abstraction du générateur de la vue est un concept C ++ général que je pense qu'il est important pour les praticiens et les étudiants de C ++ d'apprendre (considérez comment il se reporte à std :: chrono, std :: string_view, etc.).
jeremyong

39

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

Peut-être utiliser à la default_random_engineplace de mt19937? Le code aurait l'air plus générique.
Velkan

1
@Velkan Pour être honnête, ce std::default_random_enginen'est pas quelque chose que je recommande car la norme ne donne aucune garantie sur sa qualité, son efficacité ou sa répétabilité entre les implémentations.
Galik

1
Pour éviter d'utiliser un littéral de tableau de caractères et donc d'avoir à l'utiliser sizeof, changez le auto&en std::string, ce qui vous donnestd::string::length
smac89

J'ai senti que l'accès à un std::stringétait susceptible d'être plus lent car il contient un pointeur interne vers ses données. Cela signifierait une indirection supplémentaire dont un tableau statique n'a pas besoin. Aussi sizeofne peut jamais être plus lent que std::string::sizeparce qu'il s'agit d'une constante de temps de compilation.
Galik

1
@Chronial Oui, ce serait mieux. Cependant std::sizen'est pas apparu avant C++17et il y a encore beaucoup de gens qui ne codent que pour C++11/14donc je vais le laisser tel quel pour le moment.
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;
 }

Bien: ceci est indépendant du jeu de caractères (au moins pour tous les jeux de caractères qui ont a..z, A..Z et 0..9 contingents).
dmckee --- ex-moderator chaton

2
@dmckee: vrai, mais de quels autres jeux de caractères s'agit-il? (EBCDIC n'a pas de lettres contiguës).
Greg Hewgill

1
Hum. Je suppose que je suis pris au dépourvu. Je perroquetais juste quelque chose qu'un professeur m'a dit une fois ...
dmckee --- ex-moderator chaton

Une vérification rapide de la norme ne montre pas de telles exigences de continuité dans la section 2.2, là où je les attendais.
David Thornley

2
0..9 doivent cependant être contigus. pas de numéro de section, mais je suis sûr à ce sujet.
Johannes Schaub - litb le

10

Je viens de tester cela, cela fonctionne bien et ne nécessite pas de table de consultation. rand_alnum () élimine en quelque sorte les caractères alphanumériques, mais comme il sélectionne 62 caractères sur 256 possibles, ce n'est pas un gros problème.

#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
Il n'y a aucun moyen de savoir combien de temps cette fonction prendra pour s'exécuter. C'est très peu probable, mais à proprement parler, cela pourrait durer indéfiniment.
ctrlc-root

9

Plutôt que de boucler manuellement, préférez utiliser l' algorithme C ++ approprié , dans ce cas std::generate_n, avec un générateur de nombres aléatoires approprié :

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'est proche de quelque chose que j'appellerais la solution «canonique» de ce problème.

Malheureusement, semer correctement un générateur de nombres aléatoires C ++ générique (par exemple MT19937) est vraiment difficile . Le code ci - dessus utilise donc un modèle de fonction d'aide, 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};
}

C'est complexe et relativement inefficace. Heureusement, il est utilisé pour initialiser une thread_localvariable et n'est donc appelé qu'une seule fois par thread.

Enfin, les éléments nécessaires pour ce qui précède sont:

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

Le code ci-dessus utilise la déduction des arguments de modèle de classe et nécessite donc C ++ 17. Il peut être facilement adapté pour les versions antérieures en ajoutant les arguments de modèle requis.


est-ce juste une déduction std::size_tpour std::uniform_int_distribution? Je ne vois aucun autre CTAD
Caleth

@Caleth Correct. (Pourquoi) cela vous surprend-il?
Konrad Rudolph

Je suis tenté de suggérer de prendre rngcomme paramètre par défaut, avec quelque chose commetemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth

Cela m'a pris une seconde pour le voir du tout. J'étais probablement en train de remplacer mentalement std::uniform_int_distribution<>, ce qui serait sûr, mais pourrais avertir d'une conversion signée -> non signée.
Caleth

6

J'espère que ça aidera quelqu'un.

Testé sur https://www.codechef.com/ide avec 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


3
Plus 1, moins 1: Reader, méfiez - vous: RandomString(100)! ;-)
azhrei

2
Ce code est toujours cassé et présente plusieurs problèmes. Plus important encore, std::srand()ne doit être appelé qu'une seule fois au début du programme (de préférence la première chose main()). Le code, tel quel, générera beaucoup de chaînes "aléatoires" identiques s'il est appelé dans une boucle serrée.
Galik le

4

Voici un drôle de one-liner. Nécessite 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
Il doit être randomDistribution (0, sizeof (allowed_chars) - 2);
Archie

@Archie pourquoi? Ça a l'air bien (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Oleg

1
Parce que allowed_chars [] contient également le caractère '\ 0'.
Archie le

@Archie vous avez raison wandbox.org/permlink/pxMJfSbhI4MxLGES Merci!
Oleg

J'ai mis à jour la solution pour utiliser à la std::stringplace destd::string::value_type[]
Oleg

1

Quelque chose d'encore plus simple et plus basique au cas où vous seriez satisfait que votre chaîne contienne des caractères imprimables:

#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
Je suppose que c'est la solution la plus simple et tout à fait adaptée au cas avec le jeu de caractères spécifié
VolAnd

1

Chaîne aléatoire, chaque fichier d'exécution = chaîne différente

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

Ce comportement n'est pas défini, car std::generate_nil supposera que la custom_stringlongueur a LENGTH_NAME, mais ce n'est pas le cas.
Cornstalks

1

Exemple d'utilisation de 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;
}

Pouvez-vous développer votre réponse? Publier seulement un morceau de code n'est souvent pas très utile.
Noel Widmer

1

Rendons à nouveau le hasard pratique!

J'ai créé une belle solution d'en-tête C ++ 11 uniquement. Vous pouvez facilement ajouter un fichier d'en-tête à votre projet, puis ajouter vos tests ou utiliser des chaînes aléatoires à d'autres fins.

C'est une description rapide, mais vous pouvez suivre le lien pour vérifier le code complet. La partie principale de la solution est dans la 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_);
    }
};

Randomerincapsule tous les éléments aléatoires et vous pouvez y ajouter facilement vos propres fonctionnalités. Une fois que nous l'avons fait Randomer, il est très facile de générer des chaînes:

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

Écrivez vos suggestions d'amélioration ci-dessous. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad


0

Encore une autre adaptation car aucune des réponses ne suffirait à mes besoins. Tout d'abord, si rand () est utilisé pour générer des nombres aléatoires, vous obtiendrez la même sortie à chaque exécution. La graine du générateur de nombres aléatoires doit être une sorte d'aléatoire. Avec C ++ 11, vous pouvez inclure une bibliothèque "random" et vous pouvez initialiser la graine avec random_device et mt19937. Cette graine sera fournie par l'OS et elle sera assez aléatoire pour nous (par exemple: horloge). Vous pouvez donner une plage de limites sont incluses [0,25] dans mon cas. Et le dernier mais non le moindre, je n'avais besoin que d'une chaîne aléatoire de lettres minuscules, donc j'ai utilisé l'ajout de caractères. Avec une approche de groupe de personnages n'a pas fonctionné pour moi.

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

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


-1

Soyez vigilant lors de l'appel de la fonction

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

(adapté de @Ates Goral ) il en résultera à chaque fois la même séquence de caractères. Utilisation

srand(time(NULL));

avant d'appeler la fonction, bien que la fonction rand () soit toujours amorcée avec 1 @kjfletch .

Par exemple:

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

Ressemble à peu près à la réponse la mieux classée. Je ne suis pas sûr que cette réponse ajoute de la valeur.
jm.

Oui c'est le cas "srand (time (NULL));" L'index sera aléatoire à chaque itération, ce qui rendra votre chaîne plus aléatoire xD La chaîne sera différente à chaque fois qu'il exécutera la fonction ... De plus, les caractères dans les Sym représentent un tableau unique, pas tableau de pointeurs vers des chaînes.
Деян Добромиров

1
L'as tu essayé? srand (time (NULL)) réinitialise le générateur aléatoire à la même chose tout le cycle et donc il affichera essentiellement la ligne du même symbole.
Öö Tiib

Bon travail là-bas, corrigé :)
Деян Добромиров

Il fonctionne sur mon STM32F4 xD
Деян Добромиров
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.