Entwickler-Ecke

Basistechnologien - Random-Password generieren: NUR per Einzelschritt möglich


ChrisHa - Mi 28.05.14 10:04
Titel: Random-Password generieren: NUR per Einzelschritt möglich
Hallo liebe Gemeinde,

ich glaube nicht mal, dass es an meinem Code liegt, trotzdem habe ich Ihn mal hinten mit angefügt.

Zum Thema:
Mein Programm wenn ich es ausführe, soll einfach ein Passwort generieren, welches Groß, Klein und Sonderzeichen per Zufall einbindet.

Das Problem:
Wenn ich das Programm ausführe in VS mit F5 gibt er mir immer 8-15 Zeichen aus. Und zwar immer wieder das selbe Zeichen. z.B.: AAAAAAAAAAAA
Wenn ich das Problem aber jetzt per Einzelschritt (F10) durchklicke bekomme ich eine RandomLänge zwischen 8 - 15 und komplett verschiedene Symbole --> so sollte es auch sein.

Die Frage:
Liegt das nun am Code oder was mache ich falsch?


Grüße und allen einen schönen Tag.

Chris

P.s: Mir ist leider kein passender Titel hierfür eingefallen. Für Verbesserungsvorschläge bin ich natürlich offen.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
namespace PasswordGenerator
{
    class Program
    {
        static void Main(string[] args)
        {
            int iAuswahl = 0, iPwLänge = 0;
            string sPasswort = "";
            char cRückgabe = ' ';
            Random rnd = new Random();

            //Zufallszahl generien um die Passwortlänge zu ermitteln
            iPwLänge = rnd.Next(815);

            do
            {
                Random rand = new Random();
                //Zufallzahl generieren, um eine Methode im Menu aufzurufen
                iAuswahl = rand.Next(14);

                //Ruft per Zufall eine Methode auf
                cRückgabe = Menu(iAuswahl);

                sPasswort = sPasswort + cRückgabe;

                //Runterzählen lassen -- Passwortlänge
                iPwLänge--;
            } while (iPwLänge != 0);

            Console.WriteLine(sPasswort);

            //Hält die Konsole nach generien an
            Console.ReadKey();
        }

        static char Menu(int iAuswahl)
        {
            char cZeichen = ' ';

            switch (iAuswahl)
            {
                case 1:
                    //Großbuchstaben 
                    cZeichen = GenBuchstabe();
                    break;

                case 2:
                    //Kleinbuchstaben
                    cZeichen = GenBuchstabe();
                    //Konvertiert den generierten Großbuchstaben in einen Kleinbuchstaben
                    cZeichen = Char.ToLower(cZeichen);
                    break;

                case 3:
                    //Sonderzeichen
                    cZeichen = GenSonderzeichen();
                    break;

                default:
                    //Falls Fehler der Zufallzahl auftritt
                    Console.WriteLine("Fehler in der Zufallsberechnung");
                    break;
            }

            return cZeichen;
        }

        //Generiert einen Buchstaben und gibt diesen zurück
        static char GenBuchstabe()
        {
            //Alle Buchstaben in einen String gepackt und per Zufall einen Wert davon ausgewählt
            string sBuchstaben = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            int iZufallszahl = 0;

            iZufallszahl = GenZufallszahl();

            return sBuchstaben[iZufallszahl];
        }

        //Generiert eine Zufallszahl und gibt Sie zurück,
        //um einen zufälligen String Wert aufzurufen
        static int GenZufallszahl()
        {
            int iZahl = 0;

            Random rnd = new Random();
            iZahl = rnd.Next(026);

            return iZahl;
        }

        static char GenSonderzeichen()
        {
            string sSonderzeichen = "!§$%&/()=?*#";
            int iSonderzeichen = 0;


            Random rnd = new Random();
            iSonderzeichen = rnd.Next(012);

            return sSonderzeichen[iSonderzeichen];
        }
    }
}



Moderiert von user profile iconChristian S.: Topic aus C# - Die Sprache verschoben am Mi 28.05.2014 um 10:38
Moderiert von user profile iconTh69: Titel geändert.


Christian S. - Mi 28.05.14 10:37

Hallo!

Du darfst den Zufallszahlengenerator nur einmal erzeugen und nicht in jedem Schleifendurchlauf. Sonst wird der, weil das alles innerhalb kürzester Zeit passiert, immer mit demselben Wert geseedet und erzeugt immer dieselben Zahlen. Wenn Du per Einzelschritt durchgehst, sind die Zeiten anders und es wird anders geseedet, deswegen geht es dann.

Grüße
Christian


ChrisHa - Mi 28.05.14 10:48

Hallo Christian,

zunächst einmal danke für deine Antwort. Es ist tatsächlich die Lösung für mein Problem.

Weiß nicht ob es die eleganteste Lösung ist, aber ich habe in der do-while schleife nun etwas eingebaut:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  static void Main(string[] args)
        {
            int iAuswahl = 0, iPwLänge = 0;
            string sPasswort = "";
            char cRückgabe = ' ';
            Random rnd = new Random();

            //Zufallszahl generien um die Passwortlänge zu ermitteln
            iPwLänge = rnd.Next(815);

            do
            {

                System.Threading.Thread.Sleep(15);
                //Zufallzahl generieren, um eine Methode im Menu aufzurufen
                iAuswahl = rnd.Next(14);

                //Ruft per Zufall eine Methode auf
                cRückgabe = Menu(iAuswahl);

                sPasswort = sPasswort + cRückgabe;

                //Runterzählen lassen -- Passwortlänge
                iPwLänge--;
            } while (iPwLänge != 0);

            Console.WriteLine(sPasswort);

            //Hält die Konsole nach generien an
            Console.ReadKey();
        }


Habe die Zeit ausprobiert bei kürzeren Wartezeiten gibt er trotzdem oftmals zwei mal den selben Buchstaben hintereinander aus.

Hatte mir noch gedacht, dass man vielleicht die Werte irgendwie übergeben kann, aber damit hab ich mich grade noch nicht weiter befasst.

Grüße

Chris


Th69 - Mi 28.05.14 11:03

Hallo ChrisHa,

das Thread.Sleep() ist hier aber völlig überflüssig, denn nur die Erzeugung der Random-Instanz mittels des Standardkonstruktors benutzt die Systemzeit (und hier wiederum nur auf Sekundenbasis). Die Random.Next()-Aufrufe beruhen nur auf dem letzten erzeugten Zufallswert.

Wie sieht denn deine Menu-Methode aus? Erzeugst du darin dann wieder ein neues Random-Objekt?


ChrisHa - Mi 28.05.14 11:05

Hallo Th69,

an der Menu Methode habe ich nicht verändert. Sie ist noch immer genauso wie oben abgebildet. Hier nochmal mein Komplett - Code:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
namespace PasswordGenerator
{
    class Program
    {
        static void Main(string[] args)
        {
            int iAuswahl = 0, iPwLänge = 0;
            string sPasswort = "";
            char cRückgabe = ' ';
            Random rnd = new Random();

            //Zufallszahl generien um die Passwortlänge zu ermitteln
            iPwLänge = rnd.Next(815);

            do
            {

                System.Threading.Thread.Sleep(15);
                //Zufallzahl generieren, um eine Methode im Menu aufzurufen
                iAuswahl = rnd.Next(14);

                //Ruft per Zufall eine Methode auf
                cRückgabe = Menu(iAuswahl);

                sPasswort = sPasswort + cRückgabe;

                //Runterzählen lassen -- Passwortlänge
                iPwLänge--;
            } while (iPwLänge != 0);

            Console.WriteLine(sPasswort);

            //Hält die Konsole nach generien an
            Console.ReadKey();
        }

        static char Menu(int iAuswahl)
        {
            char cZeichen = ' ';

            switch (iAuswahl)
            {
                case 1:
                    //Großbuchstaben 
                    cZeichen = GenBuchstabe();
                    break;

                case 2:
                    //Kleinbuchstaben
                    cZeichen = GenBuchstabe();
                    //Konvertiert den generierten Großbuchstaben in einen Kleinbuchstaben
                    cZeichen = Char.ToLower(cZeichen);
                    break;

                case 3:
                    //Sonderzeichen
                    cZeichen = GenSonderzeichen();
                    break;

                default:
                    //Falls Fehler der Zufallzahl auftritt
                    Console.WriteLine("Fehler in der Zufallsberechnung");
                    break;
            }

            return cZeichen;
        }

        //Generiert einen Buchstaben und gibt diesen zurück
        static char GenBuchstabe()
        {
            //Alle Buchstaben in einen String gepackt und per Zufall einen Wert davon ausgewählt
            string sBuchstaben = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            int iZufallszahl = 0;

            iZufallszahl = GenZufallszahl();

            return sBuchstaben[iZufallszahl];
        }

        //Generiert eine Zufallszahl und gibt Sie zurück,
        //um einen zufälligen String Wert aufzurufen
        static int GenZufallszahl()
        {
            int iZahl = 0;

            Random rnd = new Random();
            iZahl = rnd.Next(026);

            return iZahl;
        }

        static char GenSonderzeichen()
        {
            string sSonderzeichen = "!§$%&/()=?*#";
            int iSonderzeichen = 0;


            Random rnd = new Random();
            iSonderzeichen = rnd.Next(012);

            return sSonderzeichen[iSonderzeichen];
        }
    }


Th69 - Mi 28.05.14 11:13

Sorry, hatte deinen ersten Code nicht weiter runtergescrollt gehabt.

Aber jetzt bedenke noch mal
Th69 hat folgendes geschrieben:
Erzeugst du darin dann wieder ein neues Random-Objekt?


ChrisHa - Mi 28.05.14 11:17

Ist kein Problem. Nobody is perfect.

Soweit ich dass jetzt sehe, wird in meiner Menu Methode direkt kein Random Wert mehr erzeugt.

Nur eben hier nochmal:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
static int GenZufallszahl()
        {
            int iZahl = 0;

            Random rnd = new Random();
            iZahl = rnd.Next(026);

            return iZahl;
        }

        static char GenSonderzeichen()
        {
            string sSonderzeichen = "!§$%&/()=?*#";
            int iSonderzeichen = 0;


            Random rnd = new Random();
            iSonderzeichen = rnd.Next(012);

            return sSonderzeichen[iSonderzeichen];
        }


Soll ich die Werte am besten schon in der Main erzeugen und dann einfach an die Methode mit übergeben? Falls ich dich falsch verstehe bitte ich um Berichtigung :)


Grüßé

Chris


Th69 - Mi 28.05.14 11:21

Ob direkt in der Menu()-Methode oder indirekt in den Untermethoden spielt dabei ja keine Rolle, solange diese eben aufgerufen werden - und in den Zeilen 5 und 18 (deines letzten Codes) erzeugst du ja wiederum ein neues Random-Objekt (welches wiederum mit der Systemzeit initialisiert wird).

Übergib entweder das Random-Objekt an die entsprechenden Methoden oder noch besser erzeuge dir eine Klasse, welche das Random-Objekt als Member beinhaltet und packe dort dann deine Generierungsmethoden rein (so daß diese dann auf das Random-Objekt zugreifen können).


ChrisHa - Mi 28.05.14 11:29

Also quasi dann entweder so:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PasswordGenerator
{
    class Program
    {
        static void Main(string[] args)
        {
            int iAuswahl = 0, iPwLänge = 0;
            string sPasswort = "";
            char cRückgabe = ' ';
            Random rnd = new Random();

            //Zufallszahl generien um die Passwortlänge zu ermitteln
            iPwLänge = rnd.Next(815);

            do
            {
                //Zufallzahl generieren, um eine Methode im Menu aufzurufen
                iAuswahl = rnd.Next(14);

                //Ruft per Zufall eine Methode auf
                cRückgabe = Menu(iAuswahl, rnd);

                sPasswort = sPasswort + cRückgabe;

                //Runterzählen lassen -- Passwortlänge
                iPwLänge--;
            } while (iPwLänge != 0);

            Console.WriteLine(sPasswort);

            //Hält die Konsole nach generien an
            Console.ReadKey();
        }

        static char Menu(int iAuswahl, Random rnd)
        {
            char cZeichen = ' ';

            switch (iAuswahl)
            {
                case 1:
                    //Großbuchstaben 
                    cZeichen = GenBuchstabe(rnd);
                    break;

                case 2:
                    //Kleinbuchstaben
                    cZeichen = GenBuchstabe(rnd);
                    //Konvertiert den generierten Großbuchstaben in einen Kleinbuchstaben
                    cZeichen = Char.ToLower(cZeichen);
                    break;

                case 3:
                    //Sonderzeichen
                    cZeichen = GenSonderzeichen(rnd);
                    break;

                default:
                    //Falls Fehler der Zufallzahl auftritt
                    Console.WriteLine("Fehler in der Zufallsberechnung");
                    break;
            }

            return cZeichen;
        }

        //Generiert einen Buchstaben und gibt diesen zurück
        static char GenBuchstabe(Random rnd)
        {
            //Alle Buchstaben in einen String gepackt und per Zufall einen Wert davon ausgewählt
            string sBuchstaben = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            int iZufallszahl = 0;

            iZufallszahl = GenZufallszahl(rnd);

            return sBuchstaben[iZufallszahl];
        }

        //Generiert eine Zufallszahl und gibt Sie zurück,
        //um einen zufälligen String Wert aufzurufen
        static int GenZufallszahl(Random rnd)
        {
            int iZahl = 0;

            iZahl = rnd.Next(026);

            return iZahl;
        }

        static char GenSonderzeichen(Random rnd)
        {
            string sSonderzeichen = "!§$%&/()=?*#";
            int iSonderzeichen = 0;

            iSonderzeichen = rnd.Next(012);

            return sSonderzeichen[iSonderzeichen];
        }
    }
}


Oder ich mach es dann mit Klassen. Oder?


Th69 - Mi 28.05.14 11:52

Ja, genau so habe ich es gemeint ;-)


ChrisHa - Mi 28.05.14 11:55

Gut ein Danke an Th69 und Christian für die Hilfe. Das Programm funktioniert jetzt einwandfrei.
Bzgl. der Klassen mache ich mir mal heute Nachmittag Gedanken.


In diesem Sinne einen schönen Tag allen noch.

Grüße

chris


Palladin007 - Mi 28.05.14 13:29

Für so einen Fall mache ich dann gerne Folgendes:

C#-Quelltext
1:
private readonly Random _random = new Random();                    

Oder statisch:

C#-Quelltext
1:
private static readonly Random _random = new Random();                    

Oder gleich als Singleton:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
public static class StaticRandom
{
    private static readonly Random _instance;

    public static Random Instance
    {
        get
        {
            if (_instance== null)
                _instance= new Random();

            return _instance;
        }
    }
}


So habe ich immer und überall die selbe Random-Instanz.


ChrisHa - Mi 28.05.14 14:59

Hallo Palladin007,

verstehe nicht ganz was du da machst?
Wäre ein kurze Erklärung möglich?


Palladin007 - Mi 28.05.14 15:29

Zu was genau?

Der Hintergrund ist, dass ich möglichst wenige Instanzen von Random haben möchte, weshalb ich die irgendwo zentral ablege und immer wieder verwende.

Ich meine auch gelesen zu haben, dass Random intern dafür Sorge trägt, dass nicht die gleichen Werte nacheinander auf treten, das funktioniert aber nur, wenn es auch die gleiche Instanz ist.

So im Sourcecode [http://referencesource.microsoft.com/#mscorlib/system/random.cs] habe ich das nicht auf die Schnelle raus lesen können.


Th69 - Mi 28.05.14 15:39

Dann wäre es aber nicht mehr gleichverteilt zufällig. ;-)


Palladin007 - Mi 28.05.14 16:25

Wie verhindert es denn dann, dass keine gleichen Werte heraus kommen, wenn Next 100 mal die Sekunde aufgerufen werden?

Irgendwie wird das ja in der Instanz verhindert, sonst wäre es ja egal, ob es die gleiche Instanz ist, oder nicht.


Ralf Jansen - Mi 28.05.14 16:47

Da wird nix verhindert. Jeder Wert ist gleich wahrscheinlich. Wäre es nicht so wäre Random kaputt.

Edit:
Zitat:
Wie verhindert es denn dann, dass keine gleichen Werte heraus kommen, wenn Next 100 mal die Sekunde aufgerufen werden?


Random spult je Seed die immer gleiche Zahlenfolge ab egal wann der nächste Abruf einer ~Zufallszahl~ erfolgt.

Zitat:
Irgendwie wird das ja in der Instanz verhindert, sonst wäre es ja egal, ob es die gleiche Instanz ist, oder nicht.


Du kannst auch mehrere Random Klassen mit unterschiedlichem Seed nehmen. Es sollte halt nur nicht immer eine neue Random Klasse mit dem selben Seed sein weil da immer wieder die gleiche Zahlenfolge kommt. Wenn ich immer nur den ersten Wert von dieser Random Instanz nehme kommt dann halt immer das gleiche.


BlackMatrix - Fr 13.06.14 13:05

Je nachdem wie zufällig die erzeugten Passwörter sein sollten, würde ich dir lieber eine fertige Bibliothek empfehlen. Einfach mal googlen. Ansonsten würde ich für schnelle, einfache Implementierungen Path.GetRandomFileName missbrauchen.

Edit: Ups, sehr alt.