Bagaimana cara membuat string alfa-numerik acak dalam C ++?


180

Saya ingin membuat string acak, yang terdiri dari karakter alfa-numerik. Saya ingin bisa menentukan panjang string.

Bagaimana saya melakukan ini di C ++?

Jawaban:


288

Jawaban Mehrdad Afshari akan melakukan trik, tetapi saya merasa agak terlalu verbose untuk tugas sederhana ini. Tabel pencarian terkadang dapat melakukan keajaiban:

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
@ Sent: itulah yang dilakukan tim OpenSSL, sampai seseorang berpikir untuk memasukkan kode mereka melalui valgrind. ;-)
Konrad Rudolph

11
Anda mungkin tidak ingin menggunakan rand () sederhana dengan modulus. Lihat: c-faq.com/lib/randrange.html
Randy Proctor

5
Saya pikir jalurnya s[len] = 0salah. Jika sadalah string C (NULL dihentikan) maka tanda tangan metode tidak harus memiliki lenparameter di dalamnya. Imo, jika Anda melewatkan panjang sebagai argumen, Anda menganggap array bukan string C. Dengan demikian, jika Anda tidak meneruskan string C ke fungsi, baris tersebut s[len] = 0dapat memecah hal-hal, karena array akan berubah dari 0 menjadi len-1. Dan bahkan jika Anda melewatkan string C ke fungsi, garis s[len] = 0akan menjadi berlebihan.
Felipe

16
Silakan gunakan C ++ 11 atau tingkatkan secara acak, kami sudah di 2016 sekarang
Nikko

13
Kami membutuhkan cara untuk menenggelamkan jawaban usang di stackoverflow.
Velkan

107

Inilah adaptasi saya atas jawaban Ates Goral menggunakan C ++ 11. Saya telah menambahkan lambda di sini, tetapi prinsipnya adalah Anda bisa meneruskannya dan dengan demikian mengontrol karakter apa yang terkandung dalam string Anda:

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

Berikut ini adalah contoh meneruskan lambda ke fungsi string acak: http://ideone.com/Ya8EKf

Mengapa Anda menggunakan C ++ 11 ?

  1. Karena Anda dapat menghasilkan string yang mengikuti distribusi probabilitas tertentu (atau kombinasi distribusi) untuk set karakter yang Anda minati.
  2. Karena memiliki dukungan bawaan untuk nomor acak non-deterministik
  3. Karena mendukung unicode, jadi Anda bisa mengubahnya ke versi yang diinternasionalkan.

Sebagai contoh:

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

Output sampel.


Perhatikan bahwa pada setidaknya MSVC 2012, Anda perlu const auto randSeed = std :: random_device (), kemudian meneruskan randSeed ke std :: default_random_engine (). std :: random_device {} () tidak dapat dikompilasi dengan versi ini.
NuSkooler

8
Jika Anda menggunakan C ++ 11, bukankah lebih baik tidak menggunakan rand()potongan kode pertama Anda?
Ehtesh Choudhury

C ++ 11 memungkinkan Anda untuk memisahkan generator dari mesin, tetapi apa yang terbaik tergantung pada apa kebutuhan aplikasi Anda. Inilah sebabnya mengapa potongan pertama saya menggunakan rand dan yang kedua tidak.
Carl

7
Saya berpendapat itu tidak pernah benar untuk digunakan rand()lagi. Itu bahkan tidak seragam untuk menangis dengan suara keras ...
jeremyong

1
@Carl Saya pikir dalam komunitas C ++, rand sudah usang dan anti-pola yang terkenal karena banyak alasan selain dari ketidakseragaman (lihat ceramah STL "Rand Considered Harmful"). Abstraksi generator dari pandangan adalah konsep C ++ umum yang menurut saya penting bagi praktisi dan siswa C ++ untuk dipelajari (pertimbangkan bagaimana ia membawa ke std :: chrono, std :: string_view, dll).
jeremyong

39

Solusi 2p saya:

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

Mungkin menggunakan default_random_enginebukan mt19937? Kode akan terlihat lebih umum.
Velkan

1
@Velkan Sejujurnya std::default_random_enginebukanlah sesuatu yang saya rasa bagus untuk merekomendasikan karena standar tidak menjamin kualitas, efisiensi atau pengulangan antara implementasi.
Galik

1
Untuk menghindari penggunaan char array literal dan karenanya harus menggunakan sizeof, ubah auto&to std::string, yang memberi Andastd::string::length
smac89

Saya merasa bahwa mengakses std::stringcenderung lambat karena berisi pointer internal ke datanya. Itu berarti tipuan ekstra yang tidak diperlukan array statis. Juga sizeoftidak pernah bisa lebih lambat daripada std::string::sizekarena itu adalah waktu kompilasi yang konstan.
Galik

1
@Chronial Ya memang, itu akan lebih baik. Namun std::sizetidak muncul sampai C++17dan masih ada banyak orang yang hanya coding C++11/14sehingga saya akan membiarkannya seperti sekarang.
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;
 }

Bagus: ini tidak tergantung pada set karakter (setidaknya untuk semua set karakter yang memiliki a..z, A..Z, dan 0..9 continguous).
dmckee --- ex-moderator kitten

2
@ dmckee: benar, tapi set karakter apa itu? (EBCDIC tidak memiliki huruf yang berdekatan).
Greg Hewgill

1
Um Saya kira saya tertangkap. Saya hanya membeo sesuatu yang dikatakan seorang profesor kepada saya sekali ...
dmckee --- ex-moderator kitten

Pemeriksaan cepat standar tidak menunjukkan persyaratan kesinambungan seperti itu di bagian 2.2, di mana saya mengharapkannya.
David Thornley

2
0..9 harus bersebelahan. tidak ada nomor bagian, tapi saya yakin tentang ini.
Johannes Schaub - litb

10

Saya baru saja menguji ini, ini bekerja dengan baik dan tidak memerlukan tabel pencarian. rand_alnum () semacam memaksa keluar alfanumerik tetapi karena memilih 62 dari kemungkinan 256 karakter, itu bukan masalah besar.

#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
Tidak ada cara untuk mengetahui berapa lama fungsi ini akan berjalan. Ini sangat tidak mungkin, tetapi sebenarnya, ini bisa berjalan tanpa batas.
ctrlc-root

8

Daripada melakukan perulangan secara manual, lebih baik menggunakan algoritma C ++ yang sesuai , dalam hal ini std::generate_n, dengan penghasil angka acak yang tepat :

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

Ini dekat dengan sesuatu yang saya sebut solusi "kanonik" untuk masalah ini.

Sayangnya, menabur dengan benar generator generik acak C ++ generik (mis. MT19937) benar-benar sulit . Oleh karena itu kode di atas menggunakan fungsi template helper, 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};
}

Ini rumit dan relatif tidak efisien. Untungnya ini digunakan untuk menginisialisasi athread_local variabel dan oleh karena itu hanya dipanggil sekali per utas.

Akhirnya, yang perlu mencakup untuk di atas adalah:

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

Kode di atas menggunakan deduksi argumen templat kelas dan karenanya membutuhkan C ++ 17. Ini dapat diadaptasi sepele untuk versi sebelumnya dengan menambahkan argumen templat yang diperlukan.


Apakah hanya std::size_tuntuk menyimpulkan std::uniform_int_distribution? Saya tidak dapat melihat CTAD lainnya
Caleth

@Caleth Benar. (Mengapa) apakah ini mengejutkan Anda?
Konrad Rudolph

Saya tergoda untuk menyarankan mengambil rngsebagai parameter default, dengan sesuatu sepertitemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth

Butuh satu detik untuk melihatnya sama sekali. Saya mungkin secara mental menggantikan std::uniform_int_distribution<>, yang akan aman, tetapi mungkin memperingatkan tentang konversi yang ditandatangani -> tidak ditandatangani.
Caleth

6

Saya harap ini membantu seseorang.

Diuji di https://www.codechef.com/ide dengan 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
Ditambah 1, Minus 1: Reader, berhati-hatilah: RandomString(100)! ;-)
azhrei

2
Kode ini masih rusak dan memiliki beberapa masalah. Yang paling penting std::srand()hanya benar-benar dipanggil sekali pada awal program (lebih disukai hal pertama dalam main()). Kode ini, seperti apa adanya, akan menghasilkan banyak string "acak" yang identik jika dipanggil dalam loop yang ketat.
Galik

4

Ini satu kalimat yang lucu. Membutuhkan 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
Itu harus distribusi acak (0, sizeof (diizinkan_chars) - 2);
Archie

@Archie kenapa? Terlihat baik-baik saja (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Oleg

1
Karena allow_chars [] juga mengandung karakter '\ 0'.
Archie

@Archie Anda benar wandbox.org/permlink/pxMJfSbhI4MxLGES Terima kasih!
Oleg

Saya memperbarui solusi untuk menggunakan std::stringbukannyastd::string::value_type[]
Oleg

1

Sesuatu yang lebih sederhana dan lebih mendasar jika Anda senang string Anda mengandung karakter yang dapat dicetak:

#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
Saya kira ini adalah solusi paling sederhana dan benar-benar cocok untuk kasus dengan set karakter yang ditentukan
VolAnd

1

String acak, setiap file run = string berbeda

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

Ini adalah perilaku yang tidak terdefinisi, karena std::generate_nakan menganggap custom_stringmemiliki panjang LENGTH_NAME, tetapi tidak.
Cornstalks

1

Contoh untuk penggunaan 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;
}

Bisakah Anda menguraikan jawaban Anda? Memposting hanya sepotong kode seringkali tidak terlalu membantu.
Noel Widmer

1

Mari kita buat kenyamanan acak lagi!

Saya membuat solusi bagus C ++ 11 header saja. Anda dapat dengan mudah menambahkan satu file header ke proyek Anda dan kemudian menambahkan tes Anda atau menggunakan string acak untuk tujuan lain.

Itu deskripsi singkat, tetapi Anda dapat mengikuti tautan untuk memeriksa kode lengkap. Bagian utama dari solusi adalah di kelas 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_);
    }
};

Randomermelumpuhkan semua hal acak dan Anda dapat menambahkan fungsionalitas Anda sendiri dengan mudah. Setelah kita miliki Randomer, sangat mudah untuk menghasilkan string:

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

Tulis saran Anda untuk peningkatan di bawah ini. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad


0

Namun adaptasi lain karena tidak ada jawaban akan mencukupi kebutuhan saya. Pertama-tama jika rand () digunakan untuk menghasilkan angka acak, Anda akan mendapatkan output yang sama di setiap proses. Benih untuk pembangkit bilangan acak harus berupa acak. Dengan C ++ 11 Anda dapat memasukkan pustaka "acak" dan Anda dapat menginisialisasi benih dengan random_device dan mt19937. Benih ini akan disediakan oleh OS dan itu akan cukup acak bagi kami (misalnya: jam). Anda dapat memberikan batasan batas yang disertakan [0,25] dalam kasus saya. Dan yang tak kalah pentingnya, saya hanya membutuhkan string acak huruf kecil jadi saya menggunakan tambahan char. Dengan kumpulan karakter pendekatan tidak berhasil untuk saya.

#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

Jadilah ware saat memanggil fungsi

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

(diadaptasi dari @Ates Goral ) itu akan menghasilkan urutan karakter yang sama setiap kali. Menggunakan

srand(time(NULL));

sebelum memanggil fungsi, meskipun fungsi rand () selalu diunggulkan dengan 1 @kjfletch .

Sebagai contoh:

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

Tampak hampir sama dengan jawaban peringkat teratas. Tidak yakin jawaban ini menambah nilai apa pun.
jm.

Ya itu memang "srand (waktu (NULL));" Indeks akan acak di setiap iterasi, membuat string Anda lebih acak xD String akan berbeda setiap kali ia menjalankan fungsi ... Juga karakter dalam Syms mewakili sebuah array tunggal, bukan array pointer ke string.
Деян Добромиров

1
Sudahkah Anda mencobanya? srand (time (NULL)) me-reset generator acak untuk sama semua siklus dan pada dasarnya akan mencetak baris simbol yang sama.
Öö Tiib


Ini berjalan pada STM32F4 xD saya
Деян Добромиров
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.