Як створити випадкову буквено-числову рядок у C ++?


180

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

Як це зробити в C ++?

Відповіді:


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

Ось моя адаптація відповіді Атеса Гораля за допомогою 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(). Це навіть не рівномірно, щоб плакати вголос ...
jeremyong

1
@Carl Я думаю, що у спільноті C ++ ранд застарілий, а відомий антидіапазон з багатьох причин, окрім нерівномірності (див. Розмову STL "Ранд вважається шкідливим"). Абстракція генератора від подання - це загальне поняття C ++, яке, на мою думку, є важливим для практиків і студентів C ++, щоб навчитися (розглянути, як він переходить на std :: chrono, std :: string_view тощо).
jeremyong

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

Може використовувати default_random_engineзамість mt19937? Код виглядав би більш загальним.
Велкан

1
@ Чесно кажучи std::default_random_engine, я не радимо рекомендувати, оскільки стандарт не дає гарантій щодо його якості, ефективності та повторюваності між реалізаціями.
Галик

1
Щоб уникнути використання літерального масиву char та, таким чином, використовувати його 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
Гм. Я здогадуюсь, що мене спіймали. Я просто папугував щось, що мені сказав професор одного разу ...
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

@Caleth Правильно. (Чому) це вас дивує?
Конрад Рудольф

Я відчуваю бажання запропонувати прийняття в rngякості дефолту параметра, з чим - то на кшталтtemplate <typename T = std::mt19937> inline thread_local T default_rng = get_random_generator<T>();
Caleth

Мені знадобилося секунду, щоб побачити це взагалі. Я, мабуть, подумки заміняв std::uniform_int_distribution<>, що було б безпечно, але може попередити про підписане -> непідписане перетворення.
Калет

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


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
Він повинен бути випадковим розподілом (0, розмірof (дозволено_часті) - 2);
Арчі

@Archie чому? Виглядає чудово (minIndex, maxIndex) en.cppreference.com/w/cpp/numeric/random/…
Олег

1
Тому що разрешений_шар [] також містить символ "\ 0".
Арчі

@Archie ви маєте рацію 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
}

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

Це невизначена поведінка, оскільки std::generate_nбудемо вважати, що custom_stringмає довжину LENGTH_NAME, але це не так.
Cornstalks

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


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

-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 (час (NULL))"; "Індекс буде випадковим у кожній ітерації, що зробить вашу рядок більш випадковим xD. Рядок буде різним щоразу, коли він виконує функцію ... Також символи в Syms являють собою один масив, а не масив покажчиків на рядки.
Деян Добромиров

1
Ви пробували? srand (час (NULL)) скидає випадковий генератор на один і той же весь цикл, і таким чином він буде друкувати рядок одного символу.
Öö Tiib

Хороша робота там, виправлено :)
Деян Добромиров

Він працює на моєму STM32F4 xD
Деян Добромиров
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.