C ++ 'da rastgele bir alfa-sayısal dize nasıl oluştururum?


180

Alfa-sayısal karakterlerden oluşan rastgele bir dize oluşturmak istiyorum. Dizenin uzunluğunu belirtmek istiyorum.

Bunu C ++ ile nasıl yapabilirim?

Yanıtlar:


288

Mehrdad Afshari'nin cevabı hile yapacaktı, ama bu basit görev için biraz fazla ayrıntılı buldum. Arama tabloları bazen harikalar yaratabilir:

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: Birisi kodlarını valgrind'a koymayı düşünene kadar OpenSSL ekibinin işte bu. ;-)
Konrad Rudolph

11
Muhtemelen modüllü basit bir rand () kullanmak istemezsiniz. Bakınız: c-faq.com/lib/randrange.html
Randy Proctor

5
Bence çizgi s[len] = 0yanlış. Eğer sbir C dize (NULL sona erdirilir) o zaman yöntem imzası sahip olmazdı leniçinde parametre. Imo, uzunluğu bağımsız değişken olarak geçiriyorsanız, dizinin bir C dizesi olmadığını varsayarsınız. Bu nedenle, işleve bir C dizesi s[len] = 0geçirmiyorsanız, dizi 0'dan len-1'e gideceği için çizgi bir şeyleri kırabilir. Ve bir C dizesini işleve geçirseniz bile, çizgi s[len] = 0gereksiz olur.
Felipe

16
Lütfen C ++ 11 kullanın veya rastgele artırın,
Nikko

13
Yığın akışı üzerinde eski yanıtları batırmanın bir yoluna ihtiyacımız var.
Velkan

107

İşte Ates Goral'ın cevabını C ++ 11 kullanarak uyarlamam. Lambda'yı buraya ekledim, ancak prensip, onu geçirebilmeniz ve böylece dizenizin hangi karakterleri içerdiğini kontrol edebilmenizdir:

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

Aşağıda, lambda'yı rastgele dize işlevine aktarmaya bir örnek verilmiştir: http://ideone.com/Ya8EKf

Neden C ++ 11 kullanıyorsunuz ?

  1. Çünkü ilgilendiğiniz karakter seti için belirli bir olasılık dağılımını (veya dağıtım kombinasyonunu) izleyen dizeler üretebilirsiniz .
  2. Çünkü yerleşik desteği var belirleyici olmayan rasgele sayılar
  3. Unicode'u desteklediğinden, bunu uluslararası bir sürüme değiştirebilirsiniz.

Örneğin:

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

Örnek çıktı.


En azından MSVC 2012'de, otomatik randSeed = std :: random_device () yöntemini kullanmanız ve sonra randSeed'i std :: default_random_engine () öğesine iletmeniz gerektiğini unutmayın. std :: random_device {} () bu sürümle derlenemez.
NuSkooler

8
C ++ 11 kullanıyorsanız rand(), ilk kod snippet'inizde kullanmak daha iyi değil mi?
Ehtesh Choudhury

C ++ 11, jeneratörü motordan ayırmanıza izin verir, ancak en iyi olanı, uygulamanızın gereksinimlerine bağlıdır. Bu yüzden ilk pasajım rand kullanıyor ve ikincisi kullanmıyor.
Carl

7
Artık kullanmanın asla doğru olmadığını iddia ediyorum rand(). Yüksek sesle ağlamak için bile üniforma değil ...
jeremyong

1
@Carl Sanırım C ++ topluluğunda, üniform olmasının yanı sıra birçok nedenden ötürü rand'ın reddedildiği ve iyi bilinen bir anti-patern olduğunu düşünüyorum (bkz. STL'nin "Rand Conseded Harmful"). Jeneratörün görüşten soyutlanması, öğrenmek için C ++ uygulayıcıları ve öğrencileri için önemli olduğunu düşündüğüm genel bir C ++ konseptidir (std :: chrono, std :: string_view, vb.
jeremyong

39

2p çözümüm:

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

Belki de default_random_engineyerine mt19937? Kod daha genel görünecektir.
Velkan

1
@Velkan Dürüst olmak gerekirse std::default_random_engine, standart uygulamalar arasında kalite, verimlilik veya tekrarlanabilirlik konusunda hiçbir garanti vermediği için tavsiye etmekten iyi hissetmiyorum.
Galik

1
Kullanım zorunda böylece karakter dizisi değişmezi kullanan ve önlemek için sizeof, değiştirmek auto&için std::stringsize verenstd::string::length
smac89

std::stringVerilerine dahili bir işaretçi içerdiğinden, a erişiminin daha yavaş olacağını hissettim . Bu, statik bir dizinin gerektirmediği ekstra bir dolaylama anlamına gelir. Ayrıca derleme zaman sabiti olduğundan sizeofdaha yavaş olamaz std::string::size.
Mart'ta Galik

1
@Chronial Evet, bu daha iyi olurdu. Ancak std::sizeo zamana kadar görünmedi C++17ve hala kodlayan bir sürü insan C++11/14var, bu yüzden şimdilik olduğu gibi bırakacağım.
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;
 }

Güzel: Bu karakter kümesinden bağımsızdır (en azından a .. z, A..Z ve 0..9 koşullu tüm karakter kümeleri için).
dmckee --- eski moderatör kedi yavrusu

2
@dmckee: true, ama bunlar başka hangi karakter kümeleri? (EBCDIC'in bitişik harfleri yoktur).
Greg Hewgill

1
Um. Sanırım yakalandım. Bir profesörün bana bir keresinde söylediği bir şeyi papağan
yapıyordum

Standardın hızlı bir şekilde kontrol edilmesi, bölüm 2.2'de beklediğim süreklilik gerekliliklerini göstermez.
David Thornley

2
Bununla birlikte, 0..9'un bitişik olması gerekmektedir. bölüm numarası yok, ama bundan eminim.
Johannes Schaub - litb

10

Sadece test ettim, tatlı çalışıyor ve bir arama tablosu gerektirmiyor. rand_alnum () alfanümerikleri zorlar, ancak olası 256 karakterden 62'sini seçtiği için önemli değildir.

#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
Bu işlevin çalışmasının ne kadar süreceğini bilmenin bir yolu yoktur. Çok olası değil, ama kesinlikle konuşursak, bu süresiz olarak devam edebilir.
ctrlc-root

9

Aksine elle döngü daha uygun kullanmayı tercih C ++ algoritması bu durumda, std::generate_nbir ile, uygun rasgele sayı üreteci :

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

Bu, bu sorun için “kanonik” çözüm olarak adlandıracağım bir şeye yakın.

Ne yazık ki, genel bir C ++ rasgele sayı üretecini (örneğin MT19937) doğru bir şekilde tohumlamak gerçekten zordur . Yukarıdaki kod bu nedenle bir yardımcı fonksiyon şablonu kullanır 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};
}

Bu karmaşık ve nispeten verimsizdir. Neyse ki,thread_local değişkeni ve bu nedenle iş parçacığı başına yalnızca bir kez çağrılır.

Son olarak, yukarıdakiler için gerekli kapsamlar şunlardır:

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

Yukarıdaki kod sınıf şablonu bağımsız değişken kesinti kullanır ve bu nedenle C ++ 17 gerektirir. Gerekli şablon argümanları eklenerek önceki sürümler için önemsiz bir şekilde uyarlanabilir.


Sadece deducing olduğu std::size_tiçin std::uniform_int_distribution? Başka bir CTAD göremiyorum
Caleth

@Caleth Doğru. (Neden) bu sizi şaşırtıyor mu?
Konrad Rudolph

Ben rngvarsayılan bir parametre olarak almayı önermek için template <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
cazipim

Bunu görmek bir saniyemi aldı. Muhtemelen zihinsel olarak ikame ediyordum std::uniform_int_distribution<>, bu güvenli olurdu, ancak imzalı -> imzasız dönüşüm konusunda uyarabilir.
Caleth

6

Umarım bu birine yardımcı olur.

Https://www.codechef.com/ide adresinde C ++ 4.9.2 ile test edilmiştir

#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, Eksi 1 Artı: Okuyucu, dikkat: RandomString(100)! ;-)
azhrei

2
Bu kod hala bozuk ve çeşitli sorunları var. En önemlisi , programın başlangıcında std::srand()yalnızca bir kez çağrılmalıdır (tercihen ilk şey main()). Kod, olduğu gibi, sıkı bir döngüde çağrılırsa birçok özdeş "rastgele" dizge oluşturur.
Galik

4

İşte komik bir astar. ASCII gerekir.

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) olmalıdır;
Archie

@Archie neden? İyi görünüyor (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Oleg

1
Çünkü allowed_chars [] '\ 0' karakteri de içerir.
Archie

@Archie haklısın wandbox.org/permlink/pxMJfSbhI4MxLGES Teşekkürler!
Oleg

Kullanmak çözüm güncellenen std::stringyerinestd::string::value_type[]
Oleg

1

Dizenizin yazdırılabilir karakterler içermesinden memnun olmanız durumunda daha basit ve daha temel bir şey:

#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
Sanırım bu en basit çözüm ve belirtilen karakter setine sahip dava için kesinlikle uygun
VolAnd

1

Rastgele dize, her çalışma dosyası = farklı dize

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

Bu tanımsız bir davranıştır, çünkü uzunluğunu std::generate_nvarsayar , ancak yapmaz. custom_stringLENGTH_NAME
Cornstalks

1

Qt kullanımı için örnek :)

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

Cevabınızı detaylandırabilir misiniz? Yalnızca bir kod parçası göndermek genellikle çok yararlı değildir.
Noel Widmer

1

Tekrar rastgele uygun hale getirelim!

Güzel bir C ++ 11 üstbilgi çözümü oluşturdum. Projenize kolayca bir başlık dosyası ekleyebilir ve ardından testlerinizi ekleyebilir veya başka amaçlar için rastgele dizeler kullanabilirsiniz.

Bu kısa bir açıklama, ancak tam kodu kontrol etmek için bağlantıyı takip edebilirsiniz. Çözümün ana kısmı Randomer sınıfındadır:

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

Randomertüm rastgele şeyleri enkapsüle eder ve kolayca kendi işlevselliğinizi ekleyebilirsiniz. Elimize geçtikten sonra Randomer, dizeler oluşturmak çok kolay:

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

İyileştirme önerilerinizi aşağıya yazın. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad


0

Yine başka bir adaptasyon, çünkü cevapların olmaması benim ihtiyaçlarım için yeterli olacaktır. Her şeyden önce, rastgele sayılar üretmek için rand () kullanılırsa, her çalıştırmada aynı çıktıyı alırsınız. Rastgele sayı üreteci için tohum bir tür rastgele olmalıdır. C ++ 11 ile "rastgele" kitaplığı ekleyebilir ve tohum rastgele_device ve mt19937 ile başlatabilirsiniz. Bu tohum OS tarafından tedarik edilecek ve bizim için yeterince rasgele olacaktır (örneğin: saat için). Benim durumumda dahil edilen aralık sınırları [0,25] verebilirsiniz. Ve son fakat en az değil, sadece küçük harflerin rastgele dizesine ihtiyacım vardı, bu yüzden char eklemesini kullandım. Karakter havuzu ile yaklaşım benim için çalışmadı.

#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

Fonksiyonu çağırırken eşya olun

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'a uyarlanır ) her seferinde aynı karakter dizisiyle sonuçlanır. kullanım

srand(time(NULL));

işlevi çağırmadan önce, rand () işlevi her zaman 1 @kjfletch ile ekilir .

Örneğin:

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

En üst sıradaki cevapla hemen hemen aynı görünüyor. Bu cevabın herhangi bir değer kattığından emin değilim.
jm.

Evet yapar "srand (time (NULL));" Dizin her yinelemede rastgele olur, dizenizi daha rasgele yapar xD Bu işlevi her çalıştırdığında dize farklı olur ... Ayrıca Syms'daki karakterler bir dizelere işaretçi dizisi değil, tek dizi.
Деян Добромиров

1
Bunu denediniz mi? srand (time (NULL)) rastgele jeneratörü tüm döngüye sıfırlar ve temelde aynı sembolün satırını basar.
Öö Tiib

Orada iyi iş, sabit :)
Деян Добромиров

Benim STM32F4 xD
Деян Добромиров
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.