it-swarm.com.de

Wie kann ich zufällige alphanumerische Zeichenfolgen generieren?

Wie kann ich eine zufällige 8-stellige alphanumerische Zeichenfolge in C # generieren?

847
KingNestor

Ich habe gehört, dass LINQ das neue Schwarz ist. Daher hier mein Versuch, LINQ zu verwenden:

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

(Hinweis: Durch die Verwendung der Random -Klasse ist diese für sicherheitsrelevante Zwecke ungeeignet, z. B. zum Erstellen von Kennwörtern oder Tokens. Verwenden Sie die RNGCryptoServiceProvider Klasse, wenn Sie einen starken Zufallszahlengenerator benötigen.)

1525
dtb
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

Nicht so elegant wie die Linq-Lösung.

(Hinweis: Durch die Verwendung der Random -Klasse ist diese für sicherheitsrelevante Zwecke ungeeignet, z. B. zum Erstellen von Kennwörtern oder Tokens. Verwenden Sie die RNGCryptoServiceProvider Klasse, wenn Sie einen starken Zufallszahlengenerator benötigen.)

335
Dan Rigby

AKTUALISIERT basierend auf Kommentaren. Die ursprüngliche Implementierung erzeugte a-h ~ 1,95% der Zeit und die verbleibenden Zeichen ~ 1,56% der Zeit. Das Update generiert in ca. 1,61% der Fälle alle Zeichen.

FRAMEWORK-UNTERSTÜTZUNG - .NET Core 3 (und zukünftige Plattformen, die .NET Standard 2.1 oder höher unterstützen) bieten eine kryptografisch einwandfreie Methode RandomNumberGenerator.GetInt32 () , um eine zufällige Ganzzahl innerhalb eines gewünschten Bereichs zu generieren.

Im Gegensatz zu einigen der vorgestellten Alternativen ist diese kryptografisch einwandfrei .

using System;
using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        internal static readonly char[] chars =
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); 

        public static string GetUniqueKey(int size)
        {            
            byte[] data = new byte[4*size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                var rnd = BitConverter.ToUInt32(data, i * 4);
                var idx = rnd % chars.Length;

                result.Append(chars[idx]);
            }

            return result.ToString();
        }

        public static string GetUniqueKeyOriginal_BIASED(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

Basierend auf einer Diskussion von Alternativen hier und basierend auf den Kommentaren unten aktualisiert/modifiziert.

Hier ist ein kleines Testkit, das die Verteilung der Zeichen in der alten und aktualisierten Ausgabe demonstriert. Für eine ausführliche Diskussion des Analyse der Zufälligkeit , besuchen Sie random.org.

using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}
299
Eric J.

Lösung 1 - größter 'Bereich' mit der flexibelsten Länge

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

Diese Lösung hat mehr Reichweite als die Verwendung einer GUID, da eine GUID ein paar feste Bits enthält, die immer gleich und daher nicht zufällig sind. 4 "- zumindest in einer GUID der Version 6.

Mit dieser Lösung können Sie auch eine Zeichenfolge beliebiger Länge generieren.

Lösung 2 - Eine Codezeile - gut für bis zu 22 Zeichen

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

Sie können keine Zeichenfolgen generieren, solange Lösung 1 und die Zeichenfolge aufgrund fester Bits in GUIDs nicht den gleichen Bereich aufweist, jedoch in vielen von Fällen wird dies den Job machen.

Lösung 3 - Etwas weniger Code

Guid.NewGuid().ToString("n").Substring(0, 8);

Meistens wird dies aus historischen Gründen hier aufbewahrt. Es wird etwas weniger Code verwendet, was jedoch mit der Verringerung des Bereichs verbunden ist. Da hex anstelle von base64 verwendet wird, sind mehr Zeichen erforderlich, um den gleichen Bereich im Vergleich zu den anderen Lösungen darzustellen.

Das bedeutet mehr Kollisionswahrscheinlichkeit - beim Testen mit 100.000 Iterationen von 8 Zeichenfolgen wurde ein Duplikat generiert.

190
Douglas

Hier ist ein Beispiel, das ich aus Sam Allens Beispiel unter Dot Net Perls gestohlen habe

Wenn Sie nur 8 Zeichen benötigen, verwenden Sie Path.GetRandomFileName () im System.IO-Namespace. Sam sagt, dass die Verwendung der "Path.GetRandomFileName-Methode hier manchmal überlegen ist, da sie zur besseren Zufälligkeit RNGCryptoServiceProvider verwendet. Sie ist jedoch auf 11 zufällige Zeichen beschränkt."

GetRandomFileName gibt immer eine 12-stellige Zeichenfolge mit einem Punkt am 9. Zeichen zurück. Sie müssen also den Punkt entfernen (da dies nicht zufällig ist) und dann 8 Zeichen aus der Zeichenfolge nehmen. Eigentlich könnte man nur die ersten 8 Zeichen nehmen und sich keine Gedanken über den Zeitraum machen.

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

PS: danke Sam

61
Adam Porad

Die Hauptziele meines Codes sind:

  1. Die Verteilung der Strings ist nahezu gleichmäßig (kleine Abweichungen sind egal, solange sie klein sind)
  2. Es werden mehr als ein paar Milliarden Zeichenfolgen für jeden Argumentsatz ausgegeben. Das Generieren einer 8-Zeichen-Zeichenfolge (~ 47 Bit Entropie) ist bedeutungslos, wenn Ihr PRNG nur 2 Milliarden (31 Bit Entropie) verschiedene Werte generiert.
  3. Es ist sicher, da ich erwarte, dass die Leute dies für Passwörter oder andere Sicherheitstoken verwenden.

Die erste Eigenschaft wird erreicht, indem ein 64-Bit-Wertmodul für die Alphabetgröße verwendet wird. Für kleine Alphabete (wie die 62 Zeichen aus der Frage) führt dies zu einer vernachlässigbaren Verzerrung. Die zweite und dritte Eigenschaft werden durch die Verwendung von RNGCryptoServiceProvider anstelle von System.Random erreicht.

_using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}
_
37
CodesInChaos

Das einfachste:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

Sie können eine bessere Leistung erzielen, wenn Sie das Zeichen-Array hart codieren und sich auf System.Random verlassen:

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

Wenn Sie sich jemals Sorgen machen, dass sich die englischen Alphabete irgendwann ändern und Sie möglicherweise Ihr Geschäft verlieren, können Sie hartes Codieren vermeiden, sollten aber eine etwas schlechtere Leistung erzielen (vergleichbar mit dem Path.GetRandomFileName -Ansatz).

public static string GetRandomAlphaNumeric()
{
    var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

public static IEnumerable<char> To(this char start, char end)
{
    if (end < start)
        throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
    return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}

Die letzten beiden Ansätze sehen besser aus, wenn Sie sie zu einer Erweiterungsmethode für die Instanz System.Random machen können.

29
nawfal

Nur einige Leistungsvergleiche der verschiedenen Antworten in diesem Thread:

Methoden & Setup

// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();


// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// http://stackoverflow.com/a/1344258/1037948
public string ForLoop(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
    }
    return new string(result);
}

public string ForLoopNonOptimized(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleChars[random.Next(possibleChars.Length)];
    }
    return new string(result);
}

public string Repeat(int num) {
    return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}

// http://stackoverflow.com/a/1518495/1037948
public string GenerateRandomString(int num) {
  var rBytes = new byte[num];
  random.NextBytes(rBytes);
  var rName = new char[num];
  while(num-- > 0)
    rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
  return new string(rName);
}

//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; 
    char[] rName = new char[Length];
    SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Ergebnisse

Getestet in LinqPad. Bei einer Zeichenfolge von 10 wird Folgendes generiert:

  • von Linq = chdgmevhcy [10]
  • von Loop = gtnoaryhxr [10]
  • von Select = rsndbztyby [10]
  • von GenerateRandomString = owyefjjakj [10]
  • von SecureFastRandom = VzougLYHYP [10]
  • von SecureFastRandom-NoCache = oVQXNGmO1S [10]

Und die Leistungszahlen variieren leicht, sehr gelegentlich ist NonOptimized tatsächlich schneller, und manchmal ForLoop und GenerateRandomString wechseln, wer die Führung innehat.

  • LinqIsTheNewBlack (10000x) = 96762 verstrichene Ticks (9.6762 ms)
  • ForLoop (10000x) = 28970 Ticks verstrichen (2,897 ms)
  • ForLoopNonOptimized (10000x) = 33336 verstrichene Ticks (3,3336 ms)
  • Wiederholung (10000x) = 78547 verstrichene Ticks (7,8547 ms)
  • GenerateRandomString (10000x) = 27416 verstrichene Ticks (2,7416 ms)
  • SecureFastRandom (10000x) = 13176 Ticks verstrichen (5ms) niedrigster Wert [Anderer Computer]
  • SecureFastRandom-NoCache (10000x) = 39541 Ticks verstrichen (17ms) niedrigster Wert [Anderer Computer]
21
drzaus

Eine Codezeile Membership.GeneratePassword() erledigt den Trick :)

Hier ist eine Demo für das gleiche.

18
Pooran

Der von Eric J. geschriebene Code ist ziemlich schlampig (es ist ziemlich klar, dass er von vor 6 Jahren stammt ... er würde diesen Code heute wahrscheinlich nicht schreiben), und es gibt sogar einige Probleme.

Im Gegensatz zu einigen der vorgestellten Alternativen ist diese kryptografisch einwandfrei.

Untrue ... Es gibt eine Abweichung im Passwort (wie in einem Kommentar geschrieben), bcdefgh sind etwas wahrscheinlicher als die anderen (das a liegt nicht am GetNonZeroBytes Es werden keine Bytes mit dem Wert Null generiert, sodass die Abweichung für a dadurch ausgeglichen wird. Daher ist es nicht wirklich kryptografisch korrekt.

Dies sollte alle Probleme beheben.

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}
12
xanatos

Frage: Warum sollte ich meine Zeit mit Enumerable.Range verschwenden, anstatt "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789" einzugeben?

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var randomCharacters = GetRandomCharacters(8, true);
        Console.WriteLine(new string(randomCharacters.ToArray()));
    }

    private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
    {
        var integers = Enumerable.Empty<int>();
        integers = integers.Concat(Enumerable.Range('A', 26));
        integers = integers.Concat(Enumerable.Range('0', 10));

        if ( includeLowerCase )
            integers = integers.Concat(Enumerable.Range('a', 26));

        return integers.Select(i => (char)i).ToList();
    }

    public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
    {
        var characters = getAvailableRandomCharacters(includeLowerCase);
        var random = new Random();
        var result = Enumerable.Range(0, count)
            .Select(_ => characters[random.Next(characters.Count)]);

        return result;
    }
}

Answer: Magische Saiten sind SCHLECHT. Hat jemand gemerkt, dass sich in meiner Zeichenfolge oben kein "I" befindet? Meine Mutter hat mich gelehrt, aus genau diesem Grund keine magischen Fäden zu benutzen ...

n.b. 1: Wie viele andere wie @dtb sagten, verwenden Sie System.Random nicht, wenn Sie kryptografische Sicherheit benötigen ...

n.b. 2: Diese Antwort ist nicht die effizienteste oder kürzeste, aber ich wollte, dass die Antwort von der Frage getrennt wird. Der Zweck meiner Antwort ist mehr, vor magischen Fäden zu warnen, als eine ausgefallene innovative Antwort zu geben.

6
Wai Ha Lee

Wir verwenden auch benutzerdefinierte zufällige Zeichenfolgen, aber wir haben sie als Hilfsmittel für Zeichenfolgen implementiert, damit sie etwas Flexibilität bieten ...

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

Verwendungszweck

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

oder

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
6
Mr. Pumpkin

Eine andere Möglichkeit könnte darin bestehen, Linq zu verwenden und zufällige Zeichen in einem Stringbuilder zusammenzufassen.

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());
6
AAD

Mein einfacher einzeiliger Code funktioniert für mich :)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

Um dies für eine beliebige Länge der Zeichenfolge zu erweitern

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }
6
Raj kumar

Nachdem ich die anderen Antworten überprüft und die Kommentare von CodeInChaos sowie die immer noch voreingenommene (wenn auch weniger) Antwort von CodeInChaos berücksichtigt hatte, dachte ich, dass eine endgültige Lösung zum Ausschneiden und Einfügen war erforderlich. Während ich meine Antwort aktualisiere, entscheide ich mich, alles zu tun.

Eine aktuelle Version dieses Codes finden Sie im neuen Hg-Repository von Bitbucket: https: //bitbucket.org/merarischroeder/secureswiftrandom . Ich empfehle Ihnen, den Code von folgender Adresse zu kopieren und einzufügen: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRiftRiftRiftRandom-Datei anzeigen-Standard. default (Stellen Sie sicher, dass Sie auf die Raw-Schaltfläche klicken, um das Kopieren zu vereinfachen, und stellen Sie sicher, dass Sie die neueste Version haben. Ich denke, dieser Link verweist auf eine bestimmte Version des Codes, nicht auf die neueste).

Aktualisierte Notizen:

  1. In Bezug auf einige andere Antworten - Wenn Sie die Länge der Ausgabe kennen, benötigen Sie keinen StringBuilder. Wenn Sie ToCharArray verwenden, wird das Array erstellt und gefüllt (Sie müssen kein leeres Array zuerst erstellen).
  2. In Bezug auf einige andere Antworten - Sie sollten NextBytes verwenden, anstatt jeweils eine Antwort für die Leistung abzurufen
  3. Technisch gesehen können Sie das Byte-Array für einen schnelleren Zugriff festlegen. In der Regel lohnt es sich, wenn Sie ein Byte-Array mehr als 6-8 Mal durchlaufen. (Hier nicht gemacht)
  4. Verwendung von RNGCryptoServiceProvider für beste Zufälligkeit
  5. Verwendung von Caching eines 1MB Puffers aus zufälligen Daten - Benchmarking zeigt, dass die Zugriffsgeschwindigkeit für zwischengespeicherte Einzelbytes ~ 1000x schneller ist - wobei 9 ms über 1 MB im Vergleich zu 989 ms für nicht zwischengespeicherte Daten erforderlich sind.
  6. Optimierte Unterdrückung der Bias-Zone in meiner neuen Klasse.

Lösung für Frage beenden:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

Aber du brauchst meine neue (ungetestete) Klasse:

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

Für die Geschichte - meine ältere Lösung für diese Antwort, verwendet Zufallsobjekt:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

Performance:

  1. SecureFastRandom - Erster Einzellauf = ~ 9-33ms . Unmerklich. Laufend : 5 ms (manchmal bis zu 13 ms) über 10.000 Iterationen mit einer einzelnen durchschnittlichen Iteration = 1,5 Mikrosekunden . Hinweis: Erfordert im Allgemeinen 2, gelegentlich jedoch bis zu 8 Cache-Aktualisierungen. Dies hängt davon ab, wie viele einzelne Bytes die Bias-Zone überschreiten
  2. Zufall - Erster Einzellauf = ~ 0-1ms . Unmerklich. Laufend : 5 ms über 10.000 Iterationen. Mit einer einzelnen durchschnittlichen Iteration = . 5 Mikrosekunden. . Etwa die gleiche Geschwindigkeit.

Schauen Sie sich auch Folgendes an:

Diese Links sind ein weiterer Ansatz. Diese neue Codebasis könnte gepuffert werden. Das Wichtigste war jedoch, verschiedene Ansätze zum Entfernen von Verzerrungen zu untersuchen und die Geschwindigkeiten und Vor- und Nachteile zu vergleichen.

5

Eine etwas sauberere Version der DTB-Lösung.

    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
    return string.Join("", list);

Ihre Stilvorlieben können variieren.

5
Rob Deary

Ich habe nach einer genaueren Antwort gesucht, bei der ich das Format der Zufallszeichenfolge steuern möchte, und bin auf diesen Beitrag gestoßen. Zum Beispiel: Nummernschilder (von Autos) haben ein bestimmtes Format (pro Land) und ich wollte zufällige Nummernschilder erstellen.
Ich habe mich dazu entschlossen, meine eigene Erweiterungsmethode von Random zu schreiben. (Dies ist erforderlich, um dasselbe Zufallsobjekt wiederzuverwenden, da Sie in Multithreading-Szenarien möglicherweise doppelte Werte haben.) Ich habe eine Gist ( https://Gist.github.com/SamVanhoutte/808845ca78b9c041e928 ) erstellt, kopiere aber auch die Erweiterungsklasse hier:

void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}
4
Sam Vanhoutte

Schrecklich, ich weiß, aber ich konnte mir einfach nicht helfen:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}
4
james
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
4
Tejas

Versuchen Sie, zwei Teile zu kombinieren: eindeutig (Sequenz, Zähler oder Datum) und zufällig

public class RandomStringGenerator
{
    public static string Gen()
    {
        return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
    }

    private static string GenRandomStrings(int strLen)
    {
        var result = string.Empty;

        var Gen = new RNGCryptoServiceProvider();
        var data = new byte[1];

        while (result.Length < strLen)
        {
            Gen.GetNonZeroBytes(data);
            int code = data[0];
            if (code > 48 && code < 57 || // 0-9
                code > 65 && code < 90 || // A-Z
                code > 97 && code < 122   // a-z
                )
            {
                result += Convert.ToChar(code);
            }
        }

        return result;
    }

    private static string ConvertToBase(long num, int nbase = 36)
    {
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish make algoritm more secure - change order of letter here

        // check if we can convert to another base
        if (nbase < 2 || nbase > chars.Length)
            return null;

        int r;
        var newNumber = string.Empty;

        // in r we have the offset of the char that was converted to the new base
        while (num >= nbase)
        {
            r = (int) (num % nbase);
            newNumber = chars[r] + newNumber;
            num = num / nbase;
        }
        // the last number to convert
        newNumber = chars[(int)num] + newNumber;

        return newNumber;
    }
}

Tests:

[Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }
3
RouR

Jetzt im Einzeiler-Geschmack.

private string RandomName
    {
        get
        {
            return new string(
                Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                    .Select(s =>
                    {
                        var cryptoResult = new byte[4];
                        using (var cryptoProvider = new RNGCryptoServiceProvider())
                            cryptoProvider.GetBytes(cryptoResult);
                        return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                    })
                    .ToArray());
        }
    }
2

Hier ist eine Variante von Eric Js Lösung, d. H. Kryptografisch einwandfrei, für WinRT (Windows Store App):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new StringBuilder(length);
    for (int i = 0; i < length; ++i)
    {
        result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
    }
    return result.ToString();
}

Wenn es auf die Leistung ankommt (besonders wenn die Länge hoch ist):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}
2
huyc

Eine Lösung ohne Random:

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
2
w.b

Ich weiß nicht, wie kryptografisch dies klingt, aber es ist bei weitem lesbarer und prägnanter als die komplizierteren Lösungen (imo), und es sollte "zufälliger" sein als System.Random-basierte Lösungen.

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

Ich kann mich nicht entscheiden, ob ich diese oder die nächste Version für "hübscher" halte, aber sie liefern genau die gleichen Ergebnisse:

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

Zugegeben, es ist nicht für die Geschwindigkeit optimiert. Wenn es also wichtig ist, jede Sekunde Millionen zufälliger Zeichenfolgen zu generieren, versuchen Sie es mit einer anderen!

ANMERKUNG: Diese Lösung erlaubt keine Wiederholungen von Symbolen im Alphabet und das Alphabet MUSS gleich oder größer als die Ausgabezeichenfolge sein, was diesen Ansatz unter bestimmten Umständen weniger wünschenswert macht, es hängt alles von Ihrem Anwendungsfall ab.

1
sara

Ich weiß, dass dies nicht der beste Weg ist. Aber du kannst es versuchen.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
1
Sagar

Wenn Ihre Werte nicht völlig zufällig sind, sondern tatsächlich von etwas abhängen, können Sie einen md5- oder sha1-Hash dieses "Irgendwas" berechnen und dann auf die gewünschte Länge kürzen.

Außerdem können Sie eine Guid generieren und abschneiden.

0
Alexey B.

Hier ist ein Mechanismus, um eine zufällige alphanumerische Zeichenfolge zu generieren (ich benutze dies, um Passwörter und Testdaten zu generieren), ohne das Alphabet und die Zahlen zu definieren.

CleanupBase64 entfernt die erforderlichen Teile in der Zeichenfolge und fügt weiterhin rekursiv zufällige alphanumerische Buchstaben hinzu.

        public static string GenerateRandomString(int length)
        {
            var numArray = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(numArray);
            return CleanUpBase64String(Convert.ToBase64String(numArray), length);
        }

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }
0
Dhanuka777
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
0
KregHEk