Jak utworzyć losowy ciąg alfanumeryczny w C ++?

180

Chciałbym utworzyć losowy ciąg składający się ze znaków alfanumerycznych. Chcę móc określić długość ciągu.

Jak to zrobić w C ++?

jm.
źródło

Odpowiedzi:

288

Odpowiedź Mehrdada Afshariego wystarczyłaby, ale wydała mi się zbyt rozwlekła, jak na to proste zadanie. Tabele wyszukiwania mogą czasami zdziałać cuda:

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
źródło
5
@Kent: to jest właśnie to, czym zajmuje się zespół OpenSSL, dopóki ktoś nie pomyślał o przesłaniu swojego kodu przez valgrind. ;-)
Konrad Rudolph
11
Prawdopodobnie nie chcesz używać prostego rand () z modułem. Zobacz: c-faq.com/lib/randrange.html
Randy Proctor
5
Myślę, że linia s[len] = 0jest nieprawidłowa. Jeśli sjest łańcuchem C (zakończonym znakiem NULL), to podpis metody nie musiałby zawierać lenparametru. Imo, jeśli przekazujesz długość jako argument, zakładasz, że tablica nie jest łańcuchem C. Tak więc, jeśli nie przekazujesz ciągu C do funkcji, linia s[len] = 0może zepsuć rzeczy, ponieważ tablica zmieniłaby się od 0 do len-1. A nawet jeśli przekazujesz ciąg C do funkcji, linia s[len] = 0byłaby zbędna.
Felipe
16
Skorzystaj z C ++ 11 lub doładuj losowo, jesteśmy teraz w 2016 roku
Nikko
13
Potrzebujemy sposobu na zatopienie przestarzałych odpowiedzi na przepływie stosów.
Velkan
107

Oto moja adaptacja odpowiedzi Ates Goral przy użyciu C ++ 11. Dodałem tutaj lambdę, ale zasada jest taka, że ​​możesz ją przekazać i tym samym kontrolować, jakie znaki zawiera twój ciąg:

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

Oto przykład przekazania lambdy do funkcji losowego ciągu: http://ideone.com/Ya8EKf

Dlaczego miałbyś używać C ++ 11 ?

  1. Ponieważ możesz tworzyć łańcuchy zgodne z określonym rozkładem prawdopodobieństwa (lub kombinacją dystrybucji) dla interesującego Cię zestawu znaków.
  2. Ponieważ ma wbudowaną obsługę niedeterministycznych liczb losowych
  3. Ponieważ obsługuje Unicode, więc możesz zmienić to na wersję międzynarodową.

Na przykład:

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

Przykładowe dane wyjściowe.

Carl
źródło
Zauważ, że przynajmniej na MSVC 2012, będziesz musiał const auto randSeed = std :: random_device (), a następnie przekazać randSeed do std :: default_random_engine (). std :: random_device {} () nie może się skompilować z tą wersją.
NuSkooler
8
Jeśli używasz C ++ 11, czy nie lepiej nie używać go rand()w pierwszym fragmencie kodu?
Ehtesh Choudhury
C ++ 11 pozwala oddzielić generator od silnika, ale to, co najlepsze, zależy od potrzeb Twojej aplikacji. Dlatego mój pierwszy fragment kodu używa rand, a drugi nie.
Carl
7
Twierdziłbym, że używanie go już nigdy nie jest poprawne rand(). To nawet nie jest mundur do głośnego płaczu ...
jeremyong
1
@Carl Myślę, że w społeczności C ++ rand jest przestarzały i jest dobrze znanym anty-wzorcem z wielu powodów oprócz niejednorodności (zobacz wykład STL „Rand Considered Harmful”). Abstrakcja generatora z widoku jest ogólną koncepcją C ++, którą moim zdaniem jest ważna dla praktyków i studentów C ++ do nauczenia (rozważ, w jaki sposób przenosi się do std :: chrono, std :: string_view itp.).
jeremyong
39

Moje rozwiązanie 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
źródło
Może użyć default_random_enginezamiast mt19937? Kod wyglądałby bardziej ogólnie.
Velkan
1
@Velkan Szczerze mówiąc std::default_random_enginenie jest to coś, co mi się podoba, ponieważ standard nie daje żadnych gwarancji co do jakości, wydajności czy powtarzalności pomiędzy wdrożeniami.
Galik
1
Aby uniknąć używania dosłownym tablicy char, a tym samym konieczności użycia sizeof, zmienić auto&się std::string, co dajestd::string::length
smac89
Wydaje mi się, że dostęp do a std::stringbyłby prawdopodobnie wolniejszy, ponieważ zawiera wewnętrzny wskaźnik do swoich danych. Oznaczałoby to dodatkowe pośrednictwo, którego nie wymaga tablica statyczna. Również sizeofnigdy nie może być wolniejsze niż std::string::sizedlatego, że jest stałą czasową kompilacji.
Galik
1
@Chronial Tak, rzeczywiście, byłoby lepiej. Jednak std::sizepojawiło się dopiero, C++17a wciąż jest wiele osób, które tylko kodują, C++11/14więc zostawię to tak, jak jest na razie.
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
źródło
Ładnie: jest niezależne od zestawu znaków (przynajmniej dla wszystkich zestawów znaków, które mają ciągłe a..z, A..Z i 0..9).
dmckee --- ex-moderator kitten
2
@dmckee: prawda, ale jakie inne zestawy znaków to są? (EBCDIC nie ma ciągłych liter).
Greg Hewgill
1
Um. Chyba mnie złapali. Właśnie papugowałem coś, co kiedyś powiedział mi profesor ...
dmckee --- ex-moderator kitten
Szybkie sprawdzenie normy nie wykazuje takich wymagań dotyczących ciągłości w sekcji 2.2, gdzie bym się ich spodziewał.
David Thornley
2
Jednak 0..9 muszą być ciągłe. brak numeru sekcji, ale jestem tego pewien.
Johannes Schaub - litb
10

Właśnie to przetestowałem, działa słodko i nie wymaga tabeli wyszukiwania. rand_alnum () w pewnym sensie wymusza wyświetlanie znaków alfanumerycznych, ale ponieważ wybiera 62 z możliwych 256 znaków, nie jest to wielka sprawa.

#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;
}
tylko
źródło
8
Nie ma sposobu, aby dowiedzieć się, jak długo ta funkcja będzie działać. Jest to mało prawdopodobne, ale ściśle mówiąc, może to trwać w nieskończoność.
ctrlc-root
9

Zamiast ręcznego zapętlania, preferuj użycie odpowiedniego algorytmu C ++ , w tym przypadku std::generate_nz odpowiednim generatorem liczb losowych :

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

Jest to zbliżone do czegoś, co nazwałbym „kanonicznym” rozwiązaniem tego problemu.

Niestety, prawidłowe umieszczenie generycznego generatora liczb losowych w C ++ (np. MT19937) jest naprawdę trudne . Dlatego powyższy kod używa szablonu funkcji pomocniczej 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};
}

Jest to złożone i stosunkowo nieefektywne. Na szczęście jest używany do inicjalizacji thread_localzmiennej i dlatego jest wywoływany tylko raz na wątek.

Na koniec niezbędne elementy do powyższego to:

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

Powyższy kod wykorzystuje dedukcję argumentów z szablonu klasy i dlatego wymaga C ++ 17. Można go w prosty sposób dostosować do wcześniejszych wersji, dodając wymagane argumenty szablonu.

Konrad Rudolph
źródło
jest to po prostu wnioskowanie std::size_to std::uniform_int_distribution? Nie widzę żadnego innego CTAD
Caleth
@Caleth Correct. (Dlaczego) cię to dziwi?
Konrad Rudolph
Kusi mnie, aby zasugerować przyjęcie rngjako domyślnego parametru, z czymś w rodzajutemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth
Zajęło mi sekundę, zanim to w ogóle zobaczyłem. Prawdopodobnie zastępowałem mentalnie std::uniform_int_distribution<>, co byłoby bezpieczne, ale może ostrzegać o konwersji ze znakiem -> bez znaku.
Caleth
6

Mam nadzieję, że to komuś pomoże.

Przetestowano na https://www.codechef.com/ide w 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
źródło
3
Plus 1, minus 1: Reader, uwaga: RandomString(100)! ;-)
azhrei
2
Ten kod jest nadal uszkodzony i ma kilka problemów. Co najważniejsze, std::srand()tak naprawdę powinno być wywoływane tylko raz na początku programu (najlepiej jako pierwsza rzecz main()). Kod w obecnej postaci wygeneruje wiele identycznych „losowych” ciągów, jeśli zostanie wywołany w ciasnej pętli.
Galik,
4

Oto zabawna jedna linijka. Potrzebuje ASCII.

void gen_random(char *s, int l) {
    for (int c; c=rand()%62, *s++ = (c+"07="[(c+16)/26])*(l-->0););
}
geza
źródło
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
źródło
1
Powinien być randomDistribution (0, sizeof (allowed_chars) - 2);
Archie
@Archie dlaczego? Wygląda dobrze (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/ ...
Oleg
1
Ponieważ allowed_chars [] również zawiera znak „\ 0”.
Archie
@Archie masz rację wandbox.org/permlink/pxMJfSbhI4MxLGES Dzięki!
Oleg,
Zaktualizowałem rozwiązanie do użycia std::stringzamiaststd::string::value_type[]
Oleg
1

Coś jeszcze prostszego i bardziej podstawowego, na wypadek gdybyś cieszył się, że twój ciąg zawiera jakiekolwiek drukowalne znaki:

#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
źródło
1
Przypuszczam, że jest to najprostsze rozwiązanie i absolutnie odpowiednie dla przypadku z określonym zestawem znaków
VolAnd
1

Losowy ciąg, każdy plik uruchamiania = inny ciąg

        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
źródło
Jest to niezdefiniowane zachowanie, ponieważ std::generate_nzakłada, że custom_stringma długość LENGTH_NAME, ale tak nie jest.
Cornstalks
1

Przykład użycia 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
źródło
Czy możesz rozwinąć swoją odpowiedź? Wysyłanie tylko fragmentu kodu często nie jest zbyt pomocne.
Noel Widmer
1

Sprawmy, aby losowe znów były wygodne!

Stworzyłem ładne rozwiązanie z samym nagłówkiem C ++ 11. Możesz łatwo dodać jeden plik nagłówkowy do swojego projektu, a następnie dodać testy lub użyć losowych ciągów do innych celów.

To krótki opis, ale możesz kliknąć link, aby sprawdzić pełny kod. Główna część rozwiązania znajduje się w klasie 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_);
    }
};

Randomerzawiera wszystkie przypadkowe rzeczy i możesz łatwo dodać do niego własną funkcjonalność. Gdy już to zrobimy Randomer, bardzo łatwo jest wygenerować ciągi:

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

Napisz poniżej swoje sugestie dotyczące ulepszeń. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad

Gusev Slava
źródło
0

Jeszcze jedna adaptacja, ponieważ żadna z odpowiedzi nie wystarczyłaby na moje potrzeby. Po pierwsze, jeśli rand () jest używany do generowania liczb losowych, otrzymasz takie same wyniki przy każdym uruchomieniu. Ziarno generatora liczb losowych musi być w pewnym sensie losowe. W C ++ 11 możesz dołączyć bibliotekę „random” i zainicjować ziarno za pomocą random_device i mt19937. To ziarno zostanie dostarczone przez system operacyjny i będzie dla nas wystarczająco losowe (np. Zegar). Możesz podać granice zakresu, które są zawarte [0,25] w moim przypadku. I wreszcie, potrzebowałem tylko losowego ciągu małych liter, więc użyłem dodawania znaków. Z pulą postaci podejście nie wyszło mi.

#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
źródło
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;
}
JK25
źródło
-1

Bądź ostrożny podczas wywoływania funkcji

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

(zaadaptowane z @Ates Goral ) spowoduje za każdym razem tę samą sekwencję znaków. Posługiwać się

srand(time(NULL));

przed wywołaniem funkcji, chociaż funkcja rand () jest zawsze zapełniana wartością 1 @kjfletch .

Na przykład:

void SerialNumberGenerator() {

    srand(time(NULL));
    for (int i = 0; i < 5; i++) {
        cout << gen_random(10) << endl;
    }
}
Panie Sheep
źródło
-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
źródło
-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';
}
Деян Добромиров
źródło
Wygląda mniej więcej tak samo, jak najwyżej sklasyfikowana odpowiedź. Nie jestem pewien, czy ta odpowiedź ma jakąkolwiek wartość.
jm.
Tak, działa "srand (time (NULL));" Indeks będzie losowy w każdej iteracji, dzięki czemu twój ciąg będzie bardziej losowy xD Ciąg będzie inny za każdym razem, gdy uruchomi funkcję ... Również znaki w Syms reprezentują pojedyncza tablica, a nie tablica wskaźników do łańcuchów.
Деян Добромиров
1
Próbowałeś tego? srand (time (NULL)) resetuje generator losowy do tego samego całego cyklu, więc w zasadzie wypisze wiersz tego samego symbolu.
Öö Tiib
Dobra robota, naprawiona :)
Деян Добромиров
Działa na moim STM32F4 xD
Деян Добромиров