2

I wrote this to generate a random password:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;

string read(string value) { //read value
    ifstream input;
    int olength;
    string line = "", output = "";
    size_t pos;
    bool a = true;
    int i = 0;
    input.open("pg_options.txt");
    if (!input.is_open()) {
        cout << "pg_options.txt missing.";
        return "error";
    }
    while (getline(input, line)) {
        pos = line.find(value);
        if (pos != string::npos) {
            while (a == true) {
                if (line[i] == '=') {
                    i++;
                    break;
                }
                else {
                    i++;
                }
            }
            olength = line.length() - value.length() - 1;
            for (int i2 = 0; i2 < olength; i2++) {
                output += line[i];
                i++;
            }
        }
    }
    input.close();
    return output;
}

char randupper() { //generate random upper case character
    char uppercase[26] = { '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' };
    int i = rand() % 26;
    return uppercase[i];
}

char randlower() { //generate random lower case character
    char lowercase[26] = { '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' };
    int i = rand() % 26;
    return lowercase[i];
}

char randspecial() { //generate random special character
    char special[7] = { '!', '#', '$', '%', '&', '*', '?' };
    int i = rand() % 7;
    return special[i];
}

char randnumbers() { //generate random number
    char numbers[10] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
    int i = rand() % 10;
    return numbers[i];
}

void generate() { //generate the password
    string output = "";
    int i1=0, digits = 0, upper = 0, lower = 0, special = 0, numbers = 0;
    digits = stoi(read("digits")); //get digits
    if (read("include_upper_case") == "true") { //determine number of upper case characters
        upper = rand() % (digits / 2) + 1;
        digits -= upper;
    }
    if (read("include_lower_case") == "true") {
        lower = rand() % (digits / 2) + 1;
        digits -= lower;
    }
    if (read("include_special_characters") == "true") {
        special = rand() % (digits / 2) + 1;
        digits -= special;
    }
    if (read("include_numbers") == "true") {
        numbers = digits;
    }
    else { //if numbers not included
        if (read("include_upper_case") == "true") {
            upper += digits;
        }
        else if (read("include_lower_case") == "true") {
            lower += digits;
        }
        else if (read("include_special_characters") == "true") {
            special += digits;
        }
        else {
            cout << "error generating, please check your options.";
            return;
        }
    }
    for (int i = 0; i < stoi(read("digits")); i++) {
        i1 = rand() % 4;
        if (i1 == 0) { //if uppercase
            if (upper > 0) {
                output += randupper();
                upper--;
            }
            else {
                i--;
            }
        }
        else if (i1 == 1) {
            if (lower > 0) {
                output += randlower();
                lower--;
            }
            else {
                i--;
            }
        }
        else if (i1 == 2) {
            if (special > 0) {
                output += randspecial();
                special--;
            }
            else {
                i--;
            }
        }
        else if (i1 == 3) {
            if (numbers > 0) {
                output += randnumbers();
                numbers--;
            }
            else {
                i--;
            }
        }
    }
    cout << output;
}

int main() {
    generate();
    return 0;
}

pg_options.txt:

include_special_characters=true
include_upper_case=true
include_lower_case=true
include_numbers=true
digits=10

However, it generates the same thing every time it runs, that is HM*nfx375g, so it has same sequence of random numbers. (in generate(), upper is always 2, lower is always 4, special is always 1, numbers is always 3...) Is there a function that can generate different random numbers every time?

wcyat
  • 67
  • 1
  • 2
  • 11
  • This doesn't address the question, but get in the habit of initializing objects with meaningful values rather than default-initializing them and immediately overwriting the default value. In this case, that means changing `ifstream input; ... input.open("pg_options.txt");` to `ifstream input("pg_options.txt");`. Also, your don't have to call `input.close();`. The destructor will do that. – Pete Becker Sep 04 '21 at 14:42
  • The main power in the C++ community with interest in pseudo-random numbers is the Monte-Carlo physics simulation people, and **they** require reproducibility unless the opposite is explicitly requested. But in your case, typically you'd seed the random number generator with the output of an high-resolution clock, like explained here: [SO-q34490599](https://stackoverflow.com/questions/34490599/c11-how-to-set-seed-using-random). – jpmarinier Sep 04 '21 at 14:48

2 Answers2

10

Random generators need to be seeded to have a random starting point. In your case, it needs to be done by calling srand() before using rand(), for example:

#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand(time(0)); // <-- add this!
    generate();
    return 0;
}

However, the preferred C++ way of generating random numbers is to use the functions and types available in the <random> header file. srand() and rand() are basically a leftover from C.

See this example:

#include <iostream>
#include <vector>
#include <string>
#include <random>

// get 'entropy' from device that generates random numbers itself
// to seed a mersenne twister (pseudo) random generator
static std::mt19937 generator(std::random_device{}());

char random_lowercase_char()
{
    static std::uniform_int_distribution<std::size_t> distribution('a', 'z');
    return static_cast<char>(distribution(generator));
}

char random_uppercase_char()
{
    static std::uniform_int_distribution<std::size_t> distribution('A', 'Z');
    return static_cast<char>(distribution(generator));
}

char random_number_char()
{
    static std::uniform_int_distribution<std::size_t> distribution('0', '9');
    return static_cast<char>(distribution(generator));
}

char random_special_char()
{
    static std::vector<char> specials{ '!', '#', '$', '%', '&', '*', '?' };
    static std::uniform_int_distribution<std::size_t> distribution(0, specials.size()-1);
    auto index = (distribution(generator));
    return specials[index];
}

int main()
{
    for (int i = 0; i < 40; i++)
    {
        std::cout << random_lowercase_char();
        std::cout << random_uppercase_char();
        std::cout << random_number_char();
        std::cout << random_special_char();
        std::cout << std::endl;
    }
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • While this is a good solution, you did not explain why the OP's original code was not working to begin with. You should explain the problem before offering a fix, so the OP can learn from their mistake. – Remy Lebeau Sep 04 '21 at 17:38
  • @RemyLebeau Fair enough :) I should have pointed out that random generators need to be seeded to be truly random ;) In this case it is done by std::random_device(), or in his case should have been calling srand. – Pepijn Kramer Sep 04 '21 at 17:41
  • @RemyLebeau I'm happy with that :) thanks – Pepijn Kramer Sep 04 '21 at 18:33
  • The first two `random_xxxx_char` functions are misnamed. They are not guaranteed to produce only letters. There is at least one character encoding in which the uppercase letters are not contiguous and the lowercase letters are not contiguous. The code in the question handles that correctly. – Pete Becker Sep 04 '21 at 19:31
  • @PeteBecker Interesting, never knew that. :) Even all the constexpr I've seen over the years happily use [a-z], [A-Z]. Should be an easy fix though. – Pepijn Kramer Sep 04 '21 at 19:43
  • Re: “Random generators need to be seeded to be truly random” — numerically-generated random numbers are never “truly random”. With a good generator they are pseudo-random. The next number is completely determined by the current state of the generator. Seeding doesn’t improve randomness. It just selects a different starting point. – Pete Becker Sep 04 '21 at 19:43
  • Rephrased that ;) – Pepijn Kramer Sep 04 '21 at 19:45
3

First add the <ctime> library

Then use srand(time(NULL)); before generating any random numbers with rand().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Zain Ali
  • 31
  • 1