Wie erstelle ich eine zufällige alphanumerische Zeichenfolge in C ++?

180

Ich möchte eine zufällige Zeichenfolge erstellen, die aus alphanumerischen Zeichen besteht. Ich möchte in der Lage sein, die Länge der Zeichenfolge anzugeben.

Wie mache ich das in C ++?

jm.
quelle

Antworten:

285

Mehrdad Afsharis Antwort würde den Trick machen, aber ich fand es etwas zu ausführlich für diese einfache Aufgabe. Nachschlagetabellen können manchmal Wunder bewirken:

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
quelle
5
@ Kent: Das ist es, was das OpenSSL-Team getan hat, bis jemand daran dachte, seinen Code durch Valgrind zu bringen. ;-)
Konrad Rudolph
11
Sie möchten wahrscheinlich kein einfaches rand () mit Modul verwenden. Siehe: c-faq.com/lib/randrange.html
Randy Proctor
5
Ich denke die Zeile s[len] = 0ist falsch. Wenn ses sich um eine C-Zeichenfolge handelt (NULL-terminiert), muss die Signatur der Methode nicht den lenParameter enthalten. Imo, wenn Sie die Länge als Argument übergeben, nehmen Sie an, dass das Array keine C-Zeichenfolge ist. Wenn Sie also keine C-Zeichenfolge an die Funktion übergeben, kann die Zeile zu s[len] = 0Unterbrechungen führen, da das Array von 0 auf len-1 wechselt. Und selbst wenn Sie eine C-Zeichenfolge an die Funktion übergeben, ist die Zeile s[len] = 0redundant.
Felipe
16
Bitte verwenden Sie C ++ 11 oder Boost Random, wir sind jetzt im Jahr 2016
Nikko
13
Wir brauchen eine Möglichkeit, veraltete Antworten auf den Stackoverflow zu versenken.
Velkan
107

Hier ist meine Anpassung der Antwort von Ates Goral mit C ++ 11. Ich habe das Lambda hier hinzugefügt, aber das Prinzip ist, dass Sie es weitergeben und dadurch steuern können, welche Zeichen Ihre Zeichenfolge enthält:

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

Hier ist ein Beispiel für die Übergabe eines Lambda an die Zufallszeichenfolgenfunktion: http://ideone.com/Ya8EKf

Warum sollten Sie C ++ 11 verwenden ?

  1. Weil Sie Zeichenfolgen erstellen können, die einer bestimmten Wahrscheinlichkeitsverteilung (oder Verteilungskombination) für den Zeichensatz folgen, an dem Sie interessiert sind.
  2. Weil es eingebaute Unterstützung für hat nicht deterministische Zufallszahlen
  3. Da es Unicode unterstützt, können Sie dies in eine internationalisierte Version ändern.

Beispielsweise:

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

Beispielausgabe.

Carl
quelle
Beachten Sie, dass Sie mindestens in MSVC 2012 auto randSeed = std :: random_device () konstituieren und dann randSeed an std :: default_random_engine () übergeben müssen. std :: random_device {} () kann mit dieser Version nicht kompiliert werden.
NuSkooler
8
Wenn Sie C ++ 11 verwenden, ist es nicht besser, es nicht rand()in Ihrem ersten Code-Snippet zu verwenden?
Ehtesh Choudhury
Mit C ++ 11 können Sie den Generator von der Engine trennen. Was jedoch am besten ist, hängt von den Anforderungen Ihrer Anwendung ab. Aus diesem Grund verwendet mein erstes Snippet Rand und das zweite nicht.
Carl
7
Ich würde behaupten, es ist nie mehr richtig zu benutzen rand(). Es ist nicht einmal Uniform für lautes Schreien ...
Jeremyong
1
@Carl Ich denke, in der C ++ - Community ist Rand aus vielen Gründen veraltet und ein bekanntes Anti-Pattern, abgesehen von der Ungleichmäßigkeit (siehe STLs Vortrag "Rand Considered Harmful"). Die Abstraktion des Generators von der Ansicht ist ein allgemeines C ++ - Konzept, das meiner Meinung nach für Praktiker und Studenten von C ++ wichtig ist, um es zu lernen (überlegen Sie, wie es sich auf std :: chrono, std :: string_view usw. überträgt).
Jeremyong
38

Meine 2p-Lösung:

#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
quelle
Vielleicht verwenden default_random_enginestatt mt19937? Der Code würde allgemeiner aussehen.
Velkan
1
@Velkan Um ehrlich zu sein, std::default_random_enginekann ich es nicht empfehlen, da der Standard keine Garantie für seine Qualität, Effizienz oder Wiederholbarkeit zwischen Implementierungen gibt.
Galik
1
Um zu vermeiden, dass ein char-Array-Literal verwendet wird und daher verwendet werden muss sizeof, ändern Sie das auto&in std::string, was Ihnenstd::string::length
smac89
Ich hatte das Gefühl, dass der Zugriff auf a std::stringwahrscheinlich langsamer ist, da es einen internen Zeiger auf seine Daten enthält. Dies würde eine zusätzliche Indirektion bedeuten, die ein statisches Array nicht benötigt. Auch sizeofkann nie langsamer als , std::string::sizeweil es eine Kompilierung konstant.
Galik
1
@Chronial Ja in der Tat, das wäre besser. Es ist jedoch std::sizeerst erschienen C++17und es gibt immer noch viele Leute, die nur codieren, C++11/14also werde ich es so lassen, wie es jetzt ist.
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
quelle
Schön: Dies ist unabhängig vom Zeichensatz (zumindest für alle Zeichensätze mit a..z, A..Z und 0..9 zusammenhängend).
dmckee --- Ex-Moderator Kätzchen
2
@dmckee: stimmt, aber welche anderen Zeichensätze sind das? (EBCDIC hat keine zusammenhängenden Buchstaben).
Greg Hewgill
1
Äh. Ich bin wohl erwischt. Ich habe gerade etwas nachgeprüft, was mir ein Professor einmal gesagt hat ...
dmckee --- Ex-Moderator Kätzchen
Eine schnelle Überprüfung des Standards zeigt keine solchen Kontinuitätsanforderungen in Abschnitt 2.2, wo ich sie erwarten würde.
David Thornley
2
0..9 müssen jedoch zusammenhängend sein. Keine Abschnittsnummer, aber da bin ich mir sicher.
Johannes Schaub - litb
10

Ich habe das gerade getestet, es funktioniert süß und erfordert keine Nachschlagetabelle. rand_alnum () verdrängt alphanumerische Zeichen, aber da 62 von 256 möglichen Zeichen ausgewählt werden, ist dies keine große Sache.

#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;
}
nur
quelle
8
Es gibt keine Möglichkeit zu wissen, wie lange die Ausführung dieser Funktion dauern wird. Es ist sehr unwahrscheinlich, aber genau genommen könnte dies auf unbestimmte Zeit laufen.
Ctrlc-Root
8

Verwenden Sie in diesem Fall lieber den entsprechenden C ++ - Algorithmusstd::generate_n mit einem geeigneten Zufallszahlengenerator, als manuell zu schleifen :

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

Dies kommt etwas nahe, das ich als "kanonische" Lösung für dieses Problem bezeichnen würde.

Leider ist es sehr schwierig , einen generischen C ++ - Zufallszahlengenerator (z. B. MT19937) richtig zu setzen . Der obige Code verwendet daher eine Hilfsfunktionsvorlage 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};
}

Dies ist komplex und relativ ineffizient. Zum Glück wird es verwendet, um a zu initialisierenthread_local Variablen verwendet und daher nur einmal pro Thread aufgerufen.

Schließlich sind die notwendigen Einschlüsse für die oben genannten:

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

Der obige Code verwendet die Ableitung von Klassenvorlagenargumenten und erfordert daher C ++ 17. Es kann für frühere Versionen trivial angepasst werden, indem die erforderlichen Vorlagenargumente hinzugefügt werden.

Konrad Rudolph
quelle
zieht es nur std::size_tfür std::uniform_int_distribution? Ich kann kein anderes CTAD sehen
Caleth
@ Caleth Richtig. (Warum) überrascht dich das?
Konrad Rudolph
Ich bin versucht vorzuschlagen, rngals template <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Standardparameter
Ich brauchte eine Sekunde, um es überhaupt zu sehen. Ich habe wahrscheinlich mental ersetzt std::uniform_int_distribution<>, was sicher wäre, aber vor signierter -> nicht signierter Konvertierung warnen könnte.
Caleth
6

Ich hoffe das hilft jemandem.

Getestet unter https://www.codechef.com/ide mit 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
quelle
3
Plus 1, Minus 1: Reader, Vorsicht : RandomString(100)! ;-)
Azhrei
2
Dieser Code ist immer noch defekt und hat mehrere Probleme. Am wichtigsten std::srand()ist, dass es zu Beginn des Programms nur einmal aufgerufen werden sollte (vorzugsweise als erstes main()). Der Code generiert so wie er ist viele identische "zufällige" Zeichenfolgen, wenn er in einer engen Schleife aufgerufen wird.
Galik
4

Hier ist ein lustiger Einzeiler. Benötigt ASCII.

void gen_random(char *s, int l) {
    for (int c; c=rand()%62, *s++ = (c+"07="[(c+16)/26])*(l-->0););
}
geza
quelle
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
quelle
1
Es sollte randomDistribution sein (0, sizeof (allow_chars) - 2);
Archie
@Archie warum? Sieht gut aus (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Oleg
1
Weil allow_chars [] auch das Zeichen '\ 0' enthält.
Archie
@Archie du hast recht wandbox.org/permlink/pxMJfSbhI4MxLGES Danke!
Oleg
Ich habe die Lösung aktualisiert, um sie std::stringanstelle vonstd::string::value_type[]
Oleg
1

Noch einfacher und grundlegender, falls Sie sich freuen, dass Ihre Zeichenfolge druckbare Zeichen enthält:

#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
quelle
1
Ich nehme an, dies ist die einfachste Lösung und absolut geeignet für den Fall mit angegebenem Zeichensatz
VolAnd
1

Zufällige Zeichenfolge, jede Ausführungsdatei = andere Zeichenfolge

        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
quelle
Dies ist ein undefiniertes Verhalten, da std::generate_ndavon ausgegangen custom_stringwird LENGTH_NAME, dass es eine Länge hat, dies jedoch nicht.
Cornstalks
1

Beispiel für Qt-Verwendung :)

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
quelle
Können Sie Ihre Antwort näher erläutern? Das Posten nur eines Codes ist oft nicht sehr hilfreich.
Noel Widmer
1

Lassen Sie uns zufällig wieder bequem machen!

Ich habe eine schöne Lösung für C ++ 11-Header entwickelt. Sie können Ihrem Projekt problemlos eine Header-Datei hinzufügen und dann Ihre Tests hinzufügen oder zufällige Zeichenfolgen für andere Zwecke verwenden.

Das ist eine kurze Beschreibung, aber Sie können dem Link folgen, um den vollständigen Code zu überprüfen. Der Hauptteil der Lösung ist in der Klasse 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_);
    }
};

Randomerfasst alle zufälligen Dinge zusammen und Sie können ganz einfach Ihre eigenen Funktionen hinzufügen. Nachdem wir haben Randomer, ist es sehr einfach, Zeichenfolgen zu generieren:

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

Schreiben Sie unten Ihre Verbesserungsvorschläge. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad

Gusev Slava
quelle
0

Noch eine Anpassung, weil keine der Antworten meinen Bedürfnissen genügen würde. Wenn rand () zum Generieren von Zufallszahlen verwendet wird, erhalten Sie bei jedem Lauf die gleiche Ausgabe. Der Startwert für den Zufallszahlengenerator muss eine Art Zufallsgenerator sein. Mit C ++ 11 können Sie eine "zufällige" Bibliothek einbinden und den Startwert mit random_device und mt19937 initialisieren. Dieser Startwert wird vom Betriebssystem bereitgestellt und ist für uns zufällig genug (z. B. Uhr). Sie können einen Bereich angeben, in dem Grenzen [0,25] enthalten sind. Und zu guter Letzt brauchte ich nur zufällige Kleinbuchstaben, also benutzte ich die Zeichenaddition. Mit einem Pool von Charakteren hat der Ansatz für mich nicht geklappt.

#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
quelle
-1

Seien Sie vorsichtig, wenn Sie die Funktion aufrufen

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

(angepasst an @Ates Goral ) führt jedes Mal zur gleichen Zeichenfolge. Verwenden

srand(time(NULL));

vor dem Aufruf der Funktion, obwohl die Funktion rand () immer mit 1 @kjfletch gesetzt ist .

Beispielsweise:

void SerialNumberGenerator() {

    srand(time(NULL));
    for (int i = 0; i < 5; i++) {
        cout << gen_random(10) << endl;
    }
}
Mr.Sheep
quelle
-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
quelle
-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';
}
Деян Добромиров
quelle
Sieht genauso aus wie die Antwort mit dem höchsten Rang. Ich bin mir nicht sicher, ob diese Antwort einen Mehrwert bietet.
jm.
Ja, es tut "srand (time (NULL))". Der Index wird in jeder Iteration zufällig sein, wodurch Ihre Zeichenfolge zufälliger wird. XD Die Zeichenfolge wird jedes Mal anders sein, wenn er die Funktion ausführt ... Auch die Zeichen in den Syms repräsentieren a einzelnes Array, kein Array von Zeigern auf Zeichenfolgen.
Деян Добромиров
1
Hast du es versucht? srand (time (NULL)) setzt den Zufallsgenerator während des gesamten Zyklus auf den gleichen Wert zurück und druckt so im Grunde genommen die Zeile desselben Symbols.
Öö Tiib
Gute Arbeit dort, behoben :)
Деян Добромиров
Es läuft auf meinem STM32F4 xD
4еян Добромиров