Làm cách nào để tạo chuỗi alpha-số ngẫu nhiên trong C ++?


180

Tôi muốn tạo một chuỗi ngẫu nhiên, bao gồm các ký tự chữ và số. Tôi muốn có thể được chỉ định độ dài của chuỗi.

Làm thế nào để tôi làm điều này trong C ++?

Câu trả lời:


287

Câu trả lời của Mehrdad Afshari sẽ thực hiện mánh khóe, nhưng tôi thấy nó hơi quá dài dòng cho nhiệm vụ đơn giản này. Bảng tra cứu đôi khi có thể làm điều kỳ diệu:

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: đó là những gì nhóm OpenSSL mặc dù, cho đến khi ai đó nghĩ đến việc đưa mã của họ thông qua valgrind. ;-)
Konrad Rudolph

11
Bạn có thể không muốn sử dụng một rand () đơn giản với mô đun. Xem: c-faq.com/lib/randrange.html
Randy Proctor

5
Tôi nghĩ rằng dòng s[len] = 0là không chính xác. Nếu slà một chuỗi C (kết thúc NULL) thì chữ ký của phương thức sẽ không phải có lentham số trong đó. Imo, nếu bạn truyền chiều dài làm đối số, bạn đang giả sử mảng không phải là chuỗi C. Do đó, nếu bạn không truyền chuỗi C cho hàm, dòng s[len] = 0có thể phá vỡ mọi thứ, vì mảng sẽ đi từ 0 đến len-1. Và ngay cả khi bạn truyền một chuỗi C cho hàm, dòng s[len] = 0đó sẽ là dự phòng.
Felipe

16
Vui lòng sử dụng C ++ 11 hoặc tăng ngẫu nhiên, chúng tôi đang ở trong năm 2016
Nikko

13
Chúng ta cần một cách để nhấn chìm các câu trả lời lỗi thời trên stackoverflow.
Velkan

107

Đây là bản phóng tác của tôi về câu trả lời của Ates Goral bằng C ++ 11. Tôi đã thêm lambda vào đây, nhưng nguyên tắc là bạn có thể chuyển nó vào và từ đó kiểm soát các ký tự mà chuỗi của bạn chứa:

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

Dưới đây là một ví dụ về việc chuyển một lambda sang hàm chuỗi ngẫu nhiên: http://ideone.com/Ya8EKf

Tại sao bạn nên sử dụng C ++ 11 ?

  1. Bởi vì bạn có thể tạo các chuỗi tuân theo phân phối xác suất (hoặc kết hợp phân phối) nhất định cho bộ ký tự mà bạn quan tâm.
  2. Bởi vì nó có hỗ trợ tích hợp cho các số ngẫu nhiên không xác định
  3. Bởi vì nó hỗ trợ unicode, vì vậy bạn có thể thay đổi phiên bản này thành phiên bản quốc tế hóa.

Ví dụ:

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

Sản lượng mẫu.


Lưu ý rằng trên ít nhất MSVC 2012, bạn sẽ cần const auto randSeed = std :: Random_device (), sau đó chuyển randSeed sang std :: default_random_engine (). std :: Random_device {} () không thể biên dịch với phiên bản này.
NuSkooler

8
Nếu bạn đang sử dụng C ++ 11, tốt hơn hết là không sử dụng rand()trong đoạn mã đầu tiên của bạn?
Ehtesh Choudhury 27/08/2015

C ++ 11 cho phép bạn tách trình tạo ra khỏi động cơ, nhưng điều gì là tốt nhất phụ thuộc vào nhu cầu của ứng dụng của bạn là gì. Đây là lý do tại sao đoạn đầu tiên của tôi sử dụng rand và đoạn thứ hai thì không.
Carl

7
Tôi sẽ tranh luận rằng nó không bao giờ chính xác để sử dụng rand()nữa. Nó thậm chí không đồng đều để khóc thành tiếng ...
jeremyong

1
@Carl Tôi nghĩ rằng trong cộng đồng C ++, rand bị phản đối và một mô hình chống nổi tiếng vì nhiều lý do ngoài sự không đồng nhất (xem bài nói của STL "Rand được coi là có hại"). Sự trừu tượng của trình tạo từ chế độ xem là một khái niệm chung về C ++ mà tôi nghĩ là quan trọng đối với các học viên và sinh viên của C ++ để tìm hiểu (xem xét cách nó mang đến std :: chrono, std :: string_view, v.v.).
jeremyong

38

Giải pháp 2p của tôi:

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

Có thể sử dụng default_random_enginethay vì mt19937? Các mã sẽ nhìn chung hơn.
Velkan

1
@Velkan Thành thật mà std::default_random_enginenói không phải là điều tôi cảm thấy tốt khi đề xuất vì tiêu chuẩn không đảm bảo về chất lượng, hiệu quả hoặc độ lặp lại giữa các lần thực hiện.
Galik

1
Để tránh sử dụng một mảng char theo nghĩa đen và do đó phải sử dụng sizeof, hãy thay đổi auto&thành std::string, cung cấp cho bạnstd::string::length
smac89

Tôi cảm thấy việc truy cập vào std::stringcó khả năng chậm hơn vì nó chứa một con trỏ bên trong dữ liệu của nó. Điều đó có nghĩa là một sự bổ sung thêm mà một mảng tĩnh không yêu cầu. Cũng sizeofkhông bao giờ có thể chậm hơn std::string::sizevì nó là hằng số thời gian biên dịch.
Galik

1
@Chronial Có thực sự, điều đó sẽ tốt hơn. Tuy nhiên std::sizeđã không xuất hiện cho đến khi C++17và vẫn còn rất nhiều người chỉ viết mã để C++11/14tôi sẽ rời khỏi nó như bây giờ.
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;
 }

Nice: đây là độc lập với bộ ký tự (ít nhất là cho tất cả các bộ ký tự có a..z, A..Z và 0..9 liên tiếp).
dmckee --- ex-moderator mèo con

2
@dmckee: đúng, nhưng những bộ ký tự nào khác? (EBCDIC không có chữ cái liền kề).
Greg Hewgill

1
Ừm. Tôi đoán là tôi đã bị bắt. Tôi vừa mới nhại cái gì đó mà một giáo sư đã nói với tôi một lần ...
dmckee --- người điều hành cũ mèo con

Việc kiểm tra nhanh tiêu chuẩn cho thấy không có yêu cầu liên tục như vậy trong phần 2.2, nơi tôi mong đợi chúng.
David Thornley

2
0..9 được yêu cầu phải liền kề, mặc dù. không có số phần, nhưng tôi chắc chắn về điều này.
Julian Schaub - litb

10

Tôi vừa thử nghiệm cái này, nó hoạt động rất ngọt và không cần bảng tra cứu. rand_alnum () loại lực lượng ra chữ và số nhưng vì nó chọn 62 trong số 256 ký tự có thể nên nó không phải là vấn đề lớn.

#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
Không có cách nào để biết chức năng này sẽ mất bao lâu để chạy. Điều đó rất khó xảy ra, nhưng nói đúng ra, điều này có thể chạy vô thời hạn.
ctrlc-root

8

Thay vì lặp theo cách thủ công, hãy sử dụng thuật toán C ++ thích hợp , trong trường hợp này std::generate_n, với trình tạo số ngẫu nhiên thích hợp :

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

Điều này gần với một cái gì đó mà tôi sẽ gọi là giải pháp canon canonical cho vấn đề này.

Thật không may, việc gieo chính xác một trình tạo số ngẫu nhiên C ++ chung (ví dụ MT19937) thực sự khó khăn . Do đó, đoạn mã trên sử dụng mẫu hàm trợ giúp , 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};
}

Điều này là phức tạp và tương đối không hiệu quả. May mắn thay, nó được sử dụng để khởi tạo mộtthread_local biến và do đó chỉ được gọi một lần cho mỗi luồng.

Cuối cùng, cần thiết bao gồm những điều trên là:

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

Đoạn mã trên sử dụng suy luận đối số mẫu lớp và do đó yêu cầu C ++ 17. Nó có thể được điều chỉnh một cách tầm thường cho các phiên bản trước bằng cách thêm các đối số mẫu được yêu cầu.


nó chỉ là suy luận std::size_tcho std::uniform_int_distribution? Tôi không thể thấy bất kỳ
CTAD

@Caleth Đúng. (Tại sao) điều này làm bạn ngạc nhiên?
Konrad Rudolph

Tôi muốn đề xuất lấy rnglàm tham số mặc định, với một cái gì đó nhưtemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth

Tôi phải mất một giây để nhìn thấy nó. Tôi có thể đã thay thế về mặt tinh thần std::uniform_int_distribution<>, điều này sẽ an toàn, nhưng có thể cảnh báo về việc chuyển đổi đã ký -> không dấu.
Caleth

6

Tôi hi vọng điêu nay se giup được ai đo.

Đã thử nghiệm tại https://www.codechef.com/ide với 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
Cộng 1, trừ 1: Người đọc, hãy cẩn thận : RandomString(100)! ;-)
azhrei

2
Mã này vẫn bị hỏng và có một số vấn đề. Quan trọng std::srand()nhất chỉ nên thực sự được gọi một lần khi bắt đầu chương trình (tốt nhất là điều đầu tiên trong main()). Mã, như vậy, sẽ tạo ra rất nhiều chuỗi "ngẫu nhiên" giống hệt nhau nếu được gọi trong một vòng lặp chặt chẽ.
Galik

4

Đây là một lót vui nhộn. Cần 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
Nó phải là RandomDistribution (0, sizeof (allow_chars) - 2);
Archie

@Archie tại sao? Có vẻ ổn (min Index, max Index) en.cppreference.com/w/cpp/numeric/random/ mẹo
Oleg

1
Bởi vì allow_chars [] cũng chứa ký tự '\ 0'.
Archie

@Archie bạn đúng là Wandbox.org/permlink/pxMJfSbhI4MxLGES Cảm ơn!
Oleg

Tôi đã cập nhật giải pháp để sử dụng std::stringthay vìstd::string::value_type[]
Oleg

1

Một cái gì đó thậm chí còn đơn giản và cơ bản hơn trong trường hợp bạn hài lòng cho chuỗi của mình chứa bất kỳ ký tự có thể in nào:

#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
Tôi cho rằng đây là giải pháp đơn giản nhất và hoàn toàn phù hợp cho trường hợp với bộ ký tự được chỉ định
VolAnd 21/07/17

1

Chuỗi ngẫu nhiên, mỗi tệp chạy = chuỗi khác nhau

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

Đây là hành vi không xác định, bởi vì std::generate_nsẽ giả sử custom_stringcó độ dài LENGTH_NAME, nhưng nó không.
Bắp ngô

1

Ví dụ cho việc sử dụng 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;
}

Bạn có thể giải thích về câu trả lời của bạn? Chỉ đăng một đoạn mã thường không hữu ích lắm.
Noel Widmer

1

Hãy làm cho ngẫu nhiên thuận tiện một lần nữa!

Tôi đã tạo ra một giải pháp chỉ tiêu đề C ++ 11 tốt đẹp. Bạn có thể dễ dàng thêm một tệp tiêu đề vào dự án của mình và sau đó thêm các thử nghiệm của mình hoặc sử dụng các chuỗi ngẫu nhiên cho các mục đích khác.

Đó là một mô tả nhanh, nhưng bạn có thể theo liên kết để kiểm tra mã đầy đủ. Phần chính của giải pháp là trong lớp 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_);
    }
};

Randomerbao gồm tất cả các công cụ ngẫu nhiên và bạn có thể thêm chức năng của riêng bạn vào đó một cách dễ dàng. Sau khi chúng ta có Randomer, việc tạo chuỗi rất dễ dàng:

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

Viết đề xuất của bạn để cải thiện dưới đây. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad


0

Tuy nhiên, một sự thích ứng khác bởi vì không có câu trả lời nào đủ đáp ứng nhu cầu của tôi. Trước hết, nếu rand () được sử dụng để tạo các số ngẫu nhiên, bạn sẽ nhận được cùng một đầu ra ở mỗi lần chạy. Hạt giống cho trình tạo số ngẫu nhiên phải là một số loại ngẫu nhiên. Với C ++ 11, bạn có thể bao gồm thư viện "ngẫu nhiên" và bạn có thể khởi tạo hạt giống với Random_device và mt19937. Hạt giống này sẽ được cung cấp bởi HĐH và nó sẽ đủ ngẫu nhiên cho chúng tôi (ví dụ: đồng hồ). Bạn có thể đưa ra một ranh giới phạm vi được bao gồm [0,25] trong trường hợp của tôi. Và cuối cùng nhưng không kém phần quan trọng, tôi chỉ cần chuỗi ký tự viết thường ngẫu nhiên nên tôi sử dụng phép cộng char. Với một nhóm các nhân vật tiếp cận đã không làm việc cho tôi.

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

-1

Hãy lưu kho khi gọi hàm

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

(được điều chỉnh bởi @Ates Goral ) nó sẽ dẫn đến cùng một chuỗi ký tự mỗi lần. Sử dụng

srand(time(NULL));

trước khi gọi hàm, mặc dù hàm rand () luôn được gieo bằng 1 @kjfletch .

Ví dụ:

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

Trông giống như câu trả lời được xếp hạng hàng đầu. Không chắc chắn câu trả lời này thêm bất kỳ giá trị.
jm.

Đúng là "srand (time (NULL));" Chỉ mục sẽ là ngẫu nhiên trong mỗi lần lặp, làm cho chuỗi của bạn ngẫu nhiên hơn xD Chuỗi sẽ khác nhau mỗi khi anh ta chạy hàm ... mảng đơn, không phải mảng con trỏ tới chuỗi.
Trả lời từ 2/2/2015

1
Bạn đã thử chưa? srand (time (NULL)) đặt lại bộ tạo ngẫu nhiên theo cùng một chu kỳ và do đó về cơ bản nó sẽ in hàng có cùng ký hiệu.
Öö Tiib

Làm tốt lắm, đã sửa :)
Tôi là người sáng lập

Nó chạy trên STM32F4 xD của tôi
Tôi là người sáng lập vào
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.