Как создать случайную буквенно-цифровую строку в C ++?

180

Я хотел бы создать случайную строку, состоящую из буквенно-цифровых символов. Я хочу, чтобы можно было указать длину строки.

Как мне сделать это в C ++?

JM.
источник

Ответы:

288

Мердад Афшари в ответ будет делать трюк, но я нашел это немного слишком многословен для этой простой задачи. Иногда справочные таблицы могут творить чудеса:

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: это то, что команда OpenSSL, хотя, пока кто-то не подумал о том, чтобы поставить свой код через valgrind. ;-)
Конрад Рудольф
11
Вы, вероятно, не хотите использовать простой rand () с модулем. Смотрите: c-faq.com/lib/randrange.html
Рэнди Проктор
5
Я думаю, что линия s[len] = 0неверна. Если sэто строка C (завершена NULL), то в сигнатуре метода не должно быть lenпараметра. Imo, если вы передаете длину в качестве аргумента, вы предполагаете, что массив не является строкой C. Таким образом, если вы не передадите строку C в функцию, строка s[len] = 0может разбить вещи, так как массив переместится с 0 на len-1. И даже если вы передаете строку C в функцию, строка s[len] = 0будет избыточной.
Фелипе
16
Пожалуйста, используйте C ++ 11 или повышайте рандом, мы находимся в 2016 году
Nikko
13
Нам нужен способ получить устаревшие ответы на stackoverflow.
Велкан
107

Вот моя адаптация ответа Ates Goral с использованием C ++ 11. Я добавил лямбду здесь, но принцип в том, что вы можете передать ее и тем самым контролировать, какие символы содержит ваша строка:

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

Вот пример передачи лямбда-функции в случайную строку: http://ideone.com/Ya8EKf

Почему вы используете C ++ 11 ?

  1. Потому что вы можете создавать строки, которые следуют определенному распределению вероятности (или комбинации распределения) для интересующего вас набора символов.
  2. Потому что он имеет встроенную поддержку недетерминированных случайных чисел
  3. Поскольку он поддерживает Unicode, вы можете изменить его на международную версию.

Например:

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

Пример вывода.

деревенщина
источник
Обратите внимание, что, по крайней мере, в MSVC 2012 вам потребуется const auto randSeed = std :: random_device (), а затем передать randSeed в std :: default_random_engine (). std :: random_device {} () не может скомпилироваться с этой версией.
NuSkooler
8
Если вы используете C ++ 11, не лучше ли не использовать его rand()в первом фрагменте кода?
Этеш Чоудхури
C ++ 11 позволяет вам отделить генератор от движка, но что лучше всего, зависит от потребностей вашего приложения. Вот почему мой первый фрагмент использует rand, а второй - нет.
Карл
7
Я бы сказал, что это никогда не правильно использовать rand(). Это даже не единообразно для громкого
крика
1
@Carl Я думаю, что в сообществе C ++ rand устарел и является известным анти-паттерном по многим причинам, кроме неоднородности (см. Выступление STL «Рэнд считался вредным»). Абстракция генератора от представления является общей концепцией C ++, которая, на мой взгляд, важна для практиков и студентов, изучающих C ++ (рассмотрим, как она переносится в std :: chrono, std :: string_view и т. Д.).
Джеремён
39

Мое 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
источник
Может использовать default_random_engineвместо mt19937? Код будет выглядеть более общим.
Велкан
1
@Velkan Честно говоря, std::default_random_engineэто не то, что я рекомендую, так как стандарт не дает никаких гарантий относительно его качества, эффективности или воспроизводимости между реализациями.
Галик
1
Для того, чтобы избежать использования Буквальное массив символов и , таким образом , имея в использовании sizeof, изменить auto&к std::string, который дает вамstd::string::length
smac89
Я чувствовал, что доступ к, std::stringвероятно, будет медленнее, потому что он содержит внутренний указатель на свои данные. Это означало бы дополнительное косвенное обращение, которое не требует статический массив. Также sizeofникогда не может быть медленнее, чем std::string::sizeпотому, что это постоянная времени компиляции.
Галик
1
@ Chronial Да, действительно, это было бы лучше. Однако std::sizeон не появлялся до тех C++17пор, пока многие люди только пишут код, C++11/14поэтому я оставлю все как есть.
Галик
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;
 }
Мехрдад Афшари
источник
Приятно: это не зависит от набора символов (по крайней мере для всех наборов символов, которые имеют a..z, A..Z и 0..9 непрерывно).
dmckee --- котенок экс-модератора
2
@dmckee: правда, но что это за наборы символов? (EBCDIC не имеет смежных букв).
Грег Хьюгилл
1
Um. Я думаю, что я пойман. Я просто попугайывал то, что профессор однажды сказал мне ...
dmckee - котенок экс-модератора
Быстрая проверка стандарта не показывает таких требований к преемственности в разделе 2.2, где я бы их ожидал.
Дэвид Торнли
2
0..9 должны быть смежными, хотя. нет номера раздела, но я уверен в этом.
Йоханнес Шауб -
10

Я только что проверил это, он работает хорошо и не требует таблицы поиска. rand_alnum () сортирует буквенно-цифровые символы, но поскольку он выбирает 62 из 256 возможных символов, это не имеет большого значения.

#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
Нет способа узнать, сколько времени займет выполнение этой функции. Это очень маловероятно, но, строго говоря, это может продолжаться бесконечно.
ctrlc-root
9

Вместо того, чтобы вручную зацикливаться, предпочтительнее использовать соответствующий алгоритм C ++ , в данном случае std::generate_n, с подходящим генератором случайных чисел :

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

Это близко к тому, что я бы назвал «каноническим» решением этой проблемы.

К сожалению, правильно заполнить универсальный генератор случайных чисел C ++ (например, MT19937) действительно сложно . Поэтому в приведенном выше коде используется шаблон вспомогательной функции 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};
}

Это сложно и относительно неэффективно. К счастью, он используется для инициализацииthread_local переменной и поэтому вызывается только один раз для каждого потока.

Наконец, для этого необходимо включить:

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

Приведенный выше код использует вывод аргументов шаблона класса и, следовательно, требует C ++ 17. Его можно легко адаптировать для более ранних версий, добавив необходимые аргументы шаблона.

Конрад Рудольф
источник
это просто вывод std::size_tдля std::uniform_int_distribution? Я не вижу никаких других CTAD
Caleth
@ Калет Правильно. (Почему) вас это удивляет?
Конрад Рудольф
Я испытываю желание предложить принять rngв качестве параметра по умолчанию, с чем-то вродеtemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth
Мне потребовалась секунда, чтобы увидеть это вообще. Я, вероятно, подставил мысленно std::uniform_int_distribution<>, что было бы безопасно, но мог бы предупредить о подписанном -> неподписанном преобразовании.
Caleth
6

Я надеюсь, что это поможет кому-то.

Протестировано на https://www.codechef.com/ide с 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
источник
3
Плюс 1, Минус 1: читатель, будьте осторожны RandomString(100):! ;-)
ажрей
2
Этот код все еще не работает и имеет несколько проблем. Самое главное, std::srand()должен действительно вызываться только один раз в начале программы (желательно первым делом main()). Код, как есть, будет генерировать много одинаковых «случайных» строк, если вызывается в тесном цикле.
Галик
4

Вот смешная строчка. Нуждается в 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
Это должно быть randomDistribution (0, sizeof (allow_chars) - 2);
Арчи
@ Арчи почему? Выглядит хорошо (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Олег
1
Потому что allow_chars [] также содержит символ '\ 0'.
Арчи
@ Арчи, ты прав wandbox.org/permlink/pxMJfSbhI4MxLGES Спасибо!
Олег
Я обновил решение, чтобы использовать std::stringвместоstd::string::value_type[]
Олег
1

Что-то еще более простое и более простое на случай, если вы довольны тем, что ваша строка содержит печатные символы:

#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
источник
1
Я полагаю, что это самое простое решение и абсолютно подходит для случая с указанным набором символов
VolAnd
1

Случайная строка, каждый прогон файла = другая строка

        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
источник
Это неопределенное поведение, потому что он std::generate_nбудет custom_stringиметь длину LENGTH_NAME, но это не так.
Кукурузные початки
1

Пример использования 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;
}
Виталия Алекс
источник
Можете ли вы уточнить свой ответ? Размещение только части кода часто не очень полезно.
Ноэль Видмер
1

Давайте снова сделаем случайным удобным!

Я составил хорошее решение C ++ 11 только для заголовков. Вы можете легко добавить один заголовочный файл в свой проект, а затем добавить свои тесты или использовать случайные строки для других целей.

Это краткое описание, но вы можете перейти по ссылке, чтобы проверить полный код. Основная часть решения находится в классе 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_);
    }
};

Randomerинкапсулирует все случайные вещи, и вы можете легко добавить свои собственные функции к нему. После Randomerэтого очень легко генерировать строки:

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

Напишите ваши предложения по улучшению ниже. https://gist.github.com/VjGusev/e6da2cb4d4b0b531c1d009cd1f8904ad

Гусев Слава
источник
0

Еще одна адаптация, потому что ни один из ответов не удовлетворит мои потребности. Прежде всего, если rand () используется для генерации случайных чисел, вы получите один и тот же вывод при каждом запуске. Начальное число для генератора случайных чисел должно быть случайным. В C ++ 11 вы можете включить «случайную» библиотеку и инициализировать начальное число с помощью random_device и mt19937. Это начальное число будет предоставлено операционной системой, и оно будет достаточно случайным для нас (например, часы). Можно указать границы диапазона, включенные [0,25] в моем случае. И последнее, но не менее важное: мне нужна была только случайная строка строчных букв, поэтому я использовал добавление символов. При подходе с пулом персонажей у меня не получилось.

#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;
}
JK25
источник
-1

Будьте осторожны при вызове функции

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 ) каждый раз будет приводить к одной и той же последовательности символов. использование

srand(time(NULL));

перед вызовом функции, хотя функция rand () всегда заполнена 1 @kjfletch .

Например:

void SerialNumberGenerator() {

    srand(time(NULL));
    for (int i = 0; i < 5; i++) {
        cout << gen_random(10) << endl;
    }
}
Mr.Sheep
источник
-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';
}
Деян Добромиров
источник
Выглядит примерно так же, как и самый лучший ответ. Не уверен, что этот ответ добавляет какую-либо ценность.
Джм.
Да, это делает "srand (time (NULL));" Индекс будет случайным в каждой итерации, делая вашу строку более случайной xD. Строка будет отличаться каждый раз, когда он запускает функцию ... Также символы в Syms представляют один массив, а не массив указателей на строки.
Деян Добромиров
1
Ты это пробовал? srand (time (NULL)) сбрасывает генератор случайных чисел на один и тот же весь цикл, поэтому он в основном напечатает строку с одним и тем же символом.
Öö Tiib
Хорошая работа там, исправлено :)
Деян Добромиров
Он работает на моем STM32F4 xD
Деян Добромиров