Come posso generare stringhe alfanumeriche casuali?


968

Come posso generare una stringa alfanumerica casuale di 8 caratteri in C #?


2
Quali restrizioni hai nel set di caratteri? Solo caratteri in lingua inglese e 0-9? Caso misto?
Eric J.


9
Si noti che NON è necessario utilizzare alcun metodo basato sulla Randomclasse per generare password. La semina di Randomha un'entropia molto bassa, quindi non è davvero sicura. Utilizzare un PRNG crittografico per le password.
Codici A Caos

2
Sarebbe bello includere la localizzazione della lingua in questa domanda. Soprattutto se la tua GUI deve soddisfare cinese o bulgaro!
Peter Jamsmenson,

15
Qualcosa con così tanti voti e così tante risposte di qualità non merita di essere contrassegnato come chiuso. Voto che venga riaperto.
John Coleman,

Risposte:


1686

Ho sentito che LINQ è il nuovo nero, quindi ecco il mio tentativo di utilizzare LINQ:

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

(Nota: l'uso della Randomclasse lo rende inadatto per qualsiasi cosa relativa alla sicurezza , come la creazione di password o token. Utilizzare la RNGCryptoServiceProviderclasse se è necessario un forte generatore di numeri casuali.)


24
@Alex: ho eseguito alcuni test rapidi e sembra ridimensionare quasi linearmente quando si generano stringhe più lunghe (purché in realtà ci sia memoria sufficiente). Detto questo, la risposta di Dan Rigby è stata quasi doppia rispetto a questa in ogni test.
Luca,

6
Bene. Se il tuo criterio è che usa linq e che ha una narrazione in codice pessima, allora è sicuramente le ginocchia dell'ape. Sia la narrativa del codice che il percorso effettivo di esecuzione sono piuttosto inefficienti e indiretti. Non fraintendetemi, sono un enorme hipster di codice (adoro il pitone), ma questa è praticamente una macchina goldberg rube.
eremzeit,

5
Mentre questo tecnicamente risponde alla domanda, il suo output è molto fuorviante. Generazione di 8 casuali suoni personaggi come non ci può essere molto molti risultati, che questo nel migliore dei casi produce 2 miliardi di risultati diversi. E in pratica ancora meno. Dovresti anche aggiungere un avviso BAT FAT per non usarlo per qualsiasi cosa relativa alla sicurezza.
Codici A Caos

41
@xaisoft: le lettere minuscole vengono lasciate come esercizio per il lettore.
DT

15
La seguente riga è più efficiente in termini di memoria (e quindi di tempo) rispetto a quella fornitareturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Tyson Williams,

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

Non elegante come la soluzione Linq.

(Nota: l'uso della Randomclasse lo rende inadatto per qualsiasi cosa relativa alla sicurezza , come la creazione di password o token. Utilizzare la RNGCryptoServiceProviderclasse se è necessario un forte generatore di numeri casuali.)


4
@Alex: Questa non è la risposta più veloce in assoluto, ma è la risposta "reale" più veloce (cioè di quelle che consentono il controllo sui caratteri utilizzati e sulla lunghezza della stringa).
Luca,

2
@Alex: la GetRandomFileNamesoluzione di Adam Porad è più veloce ma non consente alcun controllo dei personaggi utilizzati e la lunghezza massima possibile è di 11 caratteri. La Guidsoluzione di Douglas è velocissima ma i personaggi sono limitati ad A-F0-9 e la lunghezza massima possibile è di 32 caratteri.
Luca,

1
@Adam: Sì, potresti concedere il risultato di più chiamate GetRandomFileNamema poi (a) perderai il tuo vantaggio in termini di prestazioni e (b) il tuo codice diventerebbe più complicato.
Luca,

2
@xaisoft crea la tua istanza dell'oggetto Random () al di fuori del tuo loop. Se si creano molte istanze di Random () in un breve intervallo, la chiamata a .Next () restituirà lo stesso valore di Random () utilizza un seed basato sul tempo.
Dan Rigby,

2
@xaisoft Non usare questa risposta per qualsiasi aspetto critico per la sicurezza, come le password. System.Randomnon è adatto per la sicurezza.
CodesInChaos,

333

AGGIORNATO in base ai commenti. L'implementazione originale ha generato circa l'1,95% delle volte e gli altri caratteri l'1,56% delle volte. L'aggiornamento genera tutti i caratteri ~ 1,61% delle volte.

SUPPORTO QUADRO - .NET Core 3 (e future piattaforme che supportano .NET Standard 2.1 o versioni successive) fornisce un metodo crittograficamente valido RandomNumberGenerator.GetInt32 () per generare un numero intero casuale nell'intervallo desiderato.

A differenza di alcune delle alternative presentate, questa è crittograficamente valida .

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

Basato su una discussione di alternative qui e aggiornato / modificato in base ai commenti qui sotto.

Ecco un piccolo cablaggio di prova che dimostra la distribuzione dei caratteri nell'output precedente e aggiornato. Per una discussione approfondita sull'analisi della casualità , dai un'occhiata a 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")}%");
            }
        }
    }
}

11
Questo mi sembra l'approccio corretto: password casuali, sali, entropia e così via non dovrebbero essere generati usando Random () che è ottimizzato per la velocità e genera sequenze riproducibili di numeri; RNGCryptoServiceProvider.GetNonZeroBytes () d'altra parte produce sequenze selvagge di numeri NON riproducibili.
mindplay.dk,

25
Le lettere sono leggermente distorte (255% 62! = 0). Nonostante questo piccolo difetto, è di gran lunga la soluzione migliore qui.
CodesInChaos

13
Si noti che questo non è corretto se si desidera casualità criptica e imparziale. (E se non lo vuoi, allora perché usarlo RNGCSPin primo luogo?) L'uso di mod per indicizzare charsnell'array significa che otterrai un output distorto a meno che non chars.Lengthcapiti di essere un divisore di 256.
LukeH

15
Una possibilità per ridurre molto il bias, è richiedere 4*maxSizebyte casuali, quindi utilizzare (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Vorrei anche usare al GetBytesposto di GetNonZeroBytes. E infine puoi rimuovere la prima chiamata a GetNonZeroBytes. Non stai usando il suo risultato.
Codici InCos

14
Curiosità: AZ az 0-9 è di 62 caratteri. Le persone sottolineano il pregiudizio per le lettere perché il 256% 62! = 0. Gli ID video di YouTube sono AZ az 0-9, così come "-" e "_", che produce 64 possibili caratteri, che si divide in 256 in modo uniforme. Coincidenza? Penso di no! :)
qJake

200

Soluzione 1: la "gamma" più ampia con la lunghezza più flessibile

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

Questa soluzione ha una portata maggiore rispetto all'utilizzo di un GUID perché un GUID ha un paio di bit fissi che sono sempre gli stessi e quindi non casuali, ad esempio il carattere 13 in esadecimale è sempre "4" - almeno in un GUID versione 6.

Questa soluzione consente anche di generare una stringa di qualsiasi lunghezza.

Soluzione 2 - Una riga di codice - valida per un massimo di 22 caratteri

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

Non è possibile generare stringhe fintanto che la Soluzione 1 e la stringa non hanno lo stesso intervallo a causa di bit fissi nei GUID, ma in molti casi questo farà il lavoro.

Soluzione 3 - Codice leggermente inferiore

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

Principalmente conservarlo qui per scopi storici. Usa un po 'meno codice, che però ha il costo di avere un intervallo minore - poiché usa hex invece di base64 ci vogliono più caratteri per rappresentare lo stesso intervallo rispetto alle altre soluzioni.

Ciò significa maggiori possibilità di collisione: testarlo con 100.000 iterazioni di 8 stringhe di caratteri ha generato un duplicato.


22
Hai effettivamente generato un duplicato? Sorprendente con 5.316.911.983.139.663.491.615.228.241.121.400.000 possibili combinazioni di GUID.
Alex,

73
@Alex: sta accorciando il GUID a 8 caratteri, quindi la probabilità di collisioni è molto più alta di quella dei GUID.
dtb,

23
Nessuno può apprezzarlo oltre ai nerd :) Sì, hai perfettamente ragione, il limite di 8 caratteri fa la differenza.
Alex,

31
Guid.NewGuid (). ToString ("n") manterrà i trattini, nessuna chiamata Sostituisci () necessaria. Ma va menzionato, i GUID sono solo 0-9 e AF. Il numero di combinazioni è "abbastanza buono", ma in nessun posto vicino a ciò che una vera stringa casuale alfanumerica consente. Le possibilità di collisione sono 1: 4.294.967.296 - lo stesso di un numero intero a 32 bit casuale.
richardtallent,

32
1) I GUID sono progettati per essere unici, non casuali. Mentre le versioni attuali di Windows generano GUID V4 che sono effettivamente casuali, questo non è garantito. Ad esempio, le versioni precedenti di Windows utilizzavano i GUID V1, dove si poteva fallire. 2) Il solo utilizzo di caratteri esadecimali riduce significativamente la qualità della stringa casuale. Da 47 a 32 bit. 3) Le persone stanno sottovalutando la probabilità di collisione, poiché la danno per le singole coppie. Se generi 100k valori a 32 bit, probabilmente avrai una collisione tra di loro. Vedi problema di compleanno.
CodesInChaos

72

Ecco un esempio che ho rubato dall'esempio di Sam Allen a Dot Net Perls

Se sono necessari solo 8 caratteri, utilizzare Path.GetRandomFileName () nello spazio dei nomi System.IO. Sam afferma che l'uso del metodo "Path.GetRandomFileName qui è talvolta superiore, perché utilizza RNGCryptoServiceProvider per una migliore casualità. Tuttavia, è limitato a 11 caratteri casuali."

GetRandomFileName restituisce sempre una stringa di 12 caratteri con un punto al nono carattere. Quindi dovrai rimuovere il punto (dato che non è casuale) e quindi prendere 8 caratteri dalla stringa. In realtà, potresti prendere solo i primi 8 personaggi e non preoccuparti del periodo.

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

PS: grazie Sam


27
Funziona bene Ho eseguito 100.000 iterazioni e non ho mai avuto un nome duplicato. Tuttavia, ho fatto trovare diverse parole volgari (in inglese). Non ci avrei nemmeno pensato se non uno dei primi nella lista avesse F ***. Solo un avvertimento se lo usi per qualcosa che l'utente vedrà.
techturtle,

3
@techturtle Grazie per l'avvertimento. Suppongo che ci sia il rischio di parole volgari con qualsiasi generazione di stringhe casuali che utilizza tutte le lettere dell'alfabeto.
Adam Porad,

bello e semplice ma non va bene per una lunga stringa ... vota per questo buon trucco
Maher Abuthraa,

Questo metodo sembra restituire solo stringhe alfanumeriche minuscole.
jaybro,

2
Di tanto in tanto ci sono parole volgari, ma se continui a correre abbastanza a lungo alla fine, scrive Shakespeare. (Solo alcune vite dell'universo. :)
Slothario,

38

Gli obiettivi principali del mio codice sono:

  1. La distribuzione delle stringhe è quasi uniforme (non preoccuparti delle deviazioni minori, purché siano piccole)
  2. Produce più di qualche miliardo di stringhe per ogni set di argomenti. Generare una stringa di 8 caratteri (~ 47 bit di entropia) non ha senso se il tuo PRNG genera solo 2 miliardi (31 bit di entropia) di valori diversi.
  3. È sicuro, poiché mi aspetto che le persone lo utilizzino per password o altri token di sicurezza.

La prima proprietà si ottiene prendendo un valore di 64 bit modulo la dimensione dell'alfabeto. Per i piccoli alfabeti (come i 62 caratteri della domanda) questo porta a una distorsione trascurabile. La seconda e la terza proprietà si ottengono usando RNGCryptoServiceProviderinvece di System.Random.

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

1
Non c'è intersezione con 64 x Z e Math.Pow (2, Y). Quindi, mentre fare numeri più grandi riduce il pregiudizio, non lo elimina. Ho aggiornato la mia risposta in basso, il mio approccio era quello di scartare input casuali e sostituirli con un altro valore.
Todd,

@Todd So che non elimina il bias, ma ho scelto la semplicità di questa soluzione piuttosto che eliminare un bias praticamente irrilevante.
CodesInChaos,

Concordo nella maggior parte dei casi, probabilmente è praticamente irrilevante. Ma ora ho aggiornato il mio in modo che sia veloce come Casuale e un po 'più sicuro del tuo. Tutti open source per tutti da condividere. Sì, ho perso troppo tempo in questo ...
Todd,

Se stiamo usando il provider RNG, abbiamo un modo per evitare distorsioni in teoria? Non sono sicuro ... Se Todd intende il modo in cui genera un numero casuale aggiuntivo (quando siamo nella zona di polarizzazione), allora può essere un'ipotesi sbagliata. RNG ha una distribuzione quasi lineare di tutti i valori generati in media. Ma ciò non significa che non avremo una correlazione locale tra byte generati. Quindi byte aggiuntivi solo per la zona di polarizzazione possono ancora darci una certa distorsione, ma a causa di motivi diversi. Molto probabilmente questo pregiudizio sarà molto piccolo. MA in questo caso l'aumento del totale dei byte generati è un modo più semplice.
Massimo

1
@Maxim Puoi usare il rifiuto per eliminare completamente il bias (supponendo che il generatore sottostante sia perfettamente casuale). In cambio, il codice potrebbe durare arbitrariamente a lungo (con probabilità esponenzialmente piccola).
CodesInChaos,

32

Il più semplice:

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

È possibile ottenere prestazioni migliori se si codifica il array di caratteri e si fa affidamento su System.Random:

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

Se mai ti preoccupi, gli alfabeti inglesi possono cambiare in qualche momento e potresti perdere affari, quindi puoi evitare la codifica rigida, ma dovresti comportarti leggermente peggio (paragonabile Path.GetRandomFileNameall'approccio)

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

Gli ultimi due approcci sembrano migliori se puoi renderli un metodo di estensione su System.Randomistanza.


1
L'uso chars.Selectè un grosso problema poiché si basa sul fatto che la dimensione dell'output è al massimo la dimensione dell'alfabeto.
CodesInChaos

@CodesInChaos Non sono sicuro di averti capito. Intendi 'a'.To('z')nell'approccio?
nawfal,

1
1) chars.Select().Prendi (n) `funziona solo se chars.Count >= n. Selezionare una sequenza che in realtà non usi è un po 'poco intuitivo, specialmente con quel vincolo di lunghezza implicito. Preferirei usare Enumerable.Rangeo Enumerable.Repeat. 2) Il messaggio di errore "il carattere finale dovrebbe essere inferiore al carattere iniziale" è nel modo sbagliato / mancante a not.
Codici InCos

@CodesInChaos ma nel mio caso chars.Countè il modo > n. Inoltre non capisco la parte non intuitiva. Questo rende tutti gli usi Takenon intuitivi vero? Non ci credo. Grazie per aver segnalato l'errore di battitura.
nawfal,

4
Questo è descritto su theDailyWTF.com come articolo CodeSOD.

22

Solo alcuni confronti delle prestazioni delle varie risposte in questo thread:

Metodi e configurazione

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

risultati

Testato su LinqPad. Per stringhe di 10, genera:

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

E i numeri di spettacolo tendono a variare un po ', molto di tanto in tanto NonOptimizedè effettivamente più veloce, e qualche volta ForLoope GenerateRandomStringpassare chi è in testa.

  • LinqIsTheNewBlack (10000x) = 96762 tick scaduti (9.6762 ms)
  • ForLoop (10000x) = 28970 tick scaduti (2.897 ms)
  • ForLoopNonOptimized (10000x) = 33336 tick scaduti (3.3336 ms)
  • Ripeti (10000x) = 78547 tick scaduti (7.8547 ms)
  • GenerateRandomString (10000x) = 27416 tick scaduti (2.7416 ms)
  • SecureFastRandom (10000x) = 13176 tick scaduti (5ms) più basso [Macchina diversa]
  • SecureFastRandom-NoCache (10000x) = 39541 tick scaduti (17ms) più basso [Macchina diversa]

3
Sarebbe interessante sapere quali hanno creato i duplicati.
Rebecca,

@Junto - per capire quale risulta in duplicati, qualcosa del genere var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, dove si sostituisce EachRandomizingMethodcon ... ogni metodo
drzaus


13

Il codice scritto da Eric J. è piuttosto sciatto (è abbastanza chiaro che è di 6 anni fa ... probabilmente non scriverà quel codice oggi), e ci sono anche alcuni problemi.

A differenza di alcune delle alternative presentate, questa è crittograficamente valida.

Non vero ... C'è un bias nella password (come scritto in un commento), bcdefghsono un po 'più probabili degli altri (il anon è perché dal GetNonZeroBytesnon sta generando byte con un valore pari a zero, quindi il bias perché aè bilanciato da esso), quindi non è veramente crittograficamente valido.

Questo dovrebbe correggere tutti i problemi.

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

7

Usiamo anche stringhe personalizzate casuali ma abbiamo implementato come aiuto di una stringa, quindi offre una certa flessibilità ...

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

uso

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

o

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);

7

Il mio semplice codice a una riga funziona per me :)

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

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

Per espandere su questo per qualsiasi stringa di lunghezza

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

Mi piace anche il metodo guid - sembra davvero leggero
ozzy432836

6

Un'altra opzione potrebbe essere quella di utilizzare Linq e aggregare caratteri casuali in un costruttore di stringhe.

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

Domanda: Perché dovrei perdere tempo usando Enumerable.Rangeinvece di digitare "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"?

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

Risposta: le stringhe magiche sono CATTIVE. Qualcuno ha notato che non c'era " I" nella mia stringa in alto? Mia madre mi ha insegnato a non usare archi magici proprio per questo motivo ...

nb 1: come molti altri come hanno detto @dtb, non usare System.Randomse hai bisogno di sicurezza crittografica ...

nb 2: questa risposta non è la più efficiente o la più breve, ma volevo che lo spazio separasse la risposta dalla domanda. Lo scopo della mia risposta è più di mettere in guardia contro le corde magiche che di fornire una risposta innovativa di fantasia.


Perché mi interessa che non ci sia " I?"
Christine,

1
Alfanumerico (ignorando il caso) è [A-Z0-9]. Se, per caso, la tua stringa casuale copre sempre e solo [A-HJ-Z0-9]il risultato non copre l'intero intervallo consentito, il che può essere problematico.
Wai Ha Lee,

Come sarebbe problematico? Quindi non contiene I. È perché c'è un personaggio in meno e questo rende più facile decifrare? Quali sono le statistiche sulle password crackabili che contengono 35 caratteri nell'intervallo di 36. Penso che preferirei rischiare ... o semplicemente provare a reimpostare l'intervallo di caratteri ... piuttosto che includere tutta quella spazzatura in più nel mio codice. Ma sono io. Voglio dire, non essere un buco del culo, sto solo dicendo. A volte penso che i programmatori abbiano la tendenza a seguire la strada extra-complessa per il gusto di essere extra-complessi.
Christine,

1
Approfondisce il caso d'uso. È molto comune escludere caratteri come Ie Oda questi tipi di stringhe casuali per evitare che gli umani li confondano con 1e 0. Se non ti interessa avere una stringa leggibile dall'uomo, va bene, ma se è qualcosa che qualcuno potrebbe aver bisogno di digitare, allora è davvero intelligente rimuovere quei caratteri.
Chris Pratt,

5

Una versione leggermente più pulita della soluzione di DTB.

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

Le tue preferenze di stile possono variare.


Questo è molto meglio e più efficiente della risposta accettata.
Wedge

5
 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());
    }

5

Dopo aver esaminato le altre risposte e considerato i commenti di CodeInChaos, insieme a CodeInChaos ancora di parte (anche se meno), ho pensato che fosse necessaria una soluzione definitiva per tagliare e incollare . Quindi, aggiornando la mia risposta, ho deciso di fare tutto.

Per una versione aggiornata di questo codice, visitare il nuovo repository Hg su Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Vi consiglio di copiare e incollare il codice da: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (assicuratevi di clic il pulsante Raw per semplificare la copia e assicurarsi di disporre dell'ultima versione, penso che questo link vada a una versione specifica del codice, non all'ultima).

Note aggiornate:

  1. Relativo ad altre risposte - Se conosci la lunghezza dell'output, non hai bisogno di StringBuilder e quando usi ToCharArray, questo crea e riempie l'array (non devi prima creare un array vuoto)
  2. In relazione ad alcune altre risposte - Dovresti usare NextBytes, invece di ottenerne uno alla volta per le prestazioni
  3. Tecnicamente potresti bloccare l'array di byte per un accesso più veloce .. di solito ne vale la pena quando esegui l'iterazione più di 6-8 volte su un array di byte. (Non fatto qui)
  4. Uso di RNGCryptoServiceProvider per la migliore casualità
  5. Uso della memorizzazione nella cache di un buffer da 1 MB di dati casuali - il benchmarking mostra che la velocità di accesso ai singoli byte memorizzata nella cache è ~ 1000x più veloce - prendendo 9ms su 1 MB contro 989ms per la cache.
  6. Rifiuto ottimizzato della zona di polarizzazione all'interno della mia nuova classe.

Termina la soluzione alla domanda:

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

Ma hai bisogno della mia nuova classe (non testata):

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

Per la cronologia: la mia soluzione precedente per questa risposta, ha usato l'oggetto Casuale:

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

Prestazione:

  1. SecureFastRandom - Prima esecuzione singola = ~ 9-33ms . Impercettibile. In corso : 5 ms (a volte arriva fino a 13 ms) su 10.000 iterazioni, con una singola iterazione media = 1,5 microsecondi. . Nota: richiede generalmente 2, ma occasionalmente fino a 8 aggiornamenti della cache - dipende da quanti singoli byte superano la zona di polarizzazione
  2. Casuale - Prima corsa singola = ~ 0-1ms . Impercettibile. In corso : 5 ms oltre 10.000 iterazioni. Con una singola iterazione media = 0,5 microsecondi. . Circa alla stessa velocità.

Controlla anche:

Questi collegamenti sono un altro approccio. Il buffering potrebbe essere aggiunto a questa nuova base di codice, ma la cosa più importante è stata esplorare diversi approcci per rimuovere i pregiudizi e confrontare i parametri di velocità e vantaggi / svantaggi.


Ho trovato alcuni lievi miglioramenti delle prestazioni per il metodo, che sembrava il digiuno del gruppo - stackoverflow.com/a/17092645/1037948
drzaus

5
1) Perché tutte quelle costanti magiche? È stata specificata la lunghezza dell'output tre volte. Basta definirlo come una costante o un parametro. Puoi usare charSet.Lengthinvece di 62. 2) Una statica Randomsenza blocco significa che questo codice non è sicuro per i thread. 3) la riduzione di 0-255 mod 62 introduce una distorsione rilevabile. 4) Non è possibile utilizzare ToStringsu un array di caratteri, che restituisce sempre "System.Char[]". Devi usare new String(rName)invece.
CodesInChaos

Grazie @CodesInChaos, non avevo mai pensato a quelle cose allora. Usando ancora solo la classe casuale, ma questo dovrebbe essere migliore. Non riuscivo a pensare a un modo migliore per rilevare e correggere input di polarizzazione.
Todd,

È un po 'sciocco iniziare con un RNG debole ( System.Random) e quindi evitare con attenzione qualsiasi pregiudizio nel proprio codice. Mi viene in mente l'espressione "lucidare un turd".
CodesInChaos,

@CodesInChaos E ora l'apprendista ha superato il suo padrone
Todd,

4

Orribile, lo so, ma non riuscivo a trattenermi:


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

Stavo cercando una risposta più specifica, in cui desidero controllare il formato della stringa casuale e mi sono imbattuto in questo post. Ad esempio: le targhe (delle automobili) hanno un formato specifico (per paese) e volevo creare targhe casuali.
Ho deciso di scrivere il mio metodo di estensione di Random per questo. (questo per riutilizzare lo stesso oggetto Casuale, in quanto si potrebbero avere doppi in scenari multi-threading). Ho creato una sintesi ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), ma copierò anche la classe di estensione qui:

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

Ora nel gusto di una linea.

private string RandomName()
{
        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
L'uso di una proprietà per qualcosa che cambia ad ogni accesso è piuttosto dubbio. Consiglierei invece di usare un metodo.
CodesInCos

2
RNGCryptoServiceProviderdovrebbe essere smaltito dopo l'uso.
Tsahi Asher,

Ho risolto il problema IDisposable, ma questo è ancora altamente dubbio, creando un nuovo RNGCryptoServiceProvider per ogni lettera.
Piojo,

@CodesInChaos fatto, ora un metodo.
Matas Vaitkevicius,

3

Prova a combinare due parti: unica (sequenza, contatore o data) e casuale

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

test:

[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())
    }

1) È possibile utilizzare valori letterali dei caratteri anziché i valori ASCII associati a tali caratteri. 2) Hai un errore off-by-one nel tuo codice di corrispondenza intervallo. Devi usare <=e >=invece di <e >. 3) Aggiungerei le parentesi non necessarie attorno alle &&espressioni per chiarire che hanno la precedenza, ma ovviamente questa è solo una scelta stilistica.
CodesInChaos,

+ 1 Buono per rimuovere la distorsione e aggiungere test. Non sono sicuro del perché anteponi la tua stringa casuale con una stringa derivata dal timestamp? Inoltre, è ancora necessario disporre di RNGCryptoServiceProvider
monty

2

Una soluzione senza usare Random:

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

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

2
NewGuid utilizza casualmente internamente. Quindi questo sta ancora usando random, lo sta solo nascondendo.
Wedge

2

Ecco una variante della soluzione di Eric J, ovvero crittograficamente sana, per WinRT (app di Windows Store):

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

Se le prestazioni contano (soprattutto quando la lunghezza è alta):

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

1
Questo non è crittograficamente valido. C'è un piccolo pregiudizio dovuto all'operazione del modulo che non distribuisce l'intera larghezza di ulong equamente in 62 caratteri.
Lie Ryan,

1

So che questo non è il modo migliore. Ma puoi provare questo.

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

2
Com'è quella linea? Console.WriteLine ($ "Stringa casuale: {Path.GetRandomFileName (). Sostituisci (". "," ")}"); è una riga.
PmanAce,

1

Non so quanto sia crittograficamente valido, ma è più leggibile e conciso delle soluzioni più intricate di gran lunga (imo), e dovrebbe essere più "casuale" di System.Randomsoluzioni basate su.

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

Non riesco a decidere se penso che questa versione o la successiva siano "più belle", ma danno esattamente gli stessi risultati:

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

Certo, non è ottimizzato per la velocità, quindi se è fondamentale generare milioni di stringhe casuali ogni secondo, provane un'altra!

NOTA: questa soluzione non consente la ripetizione di simboli nell'alfabeto e l'alfabeto DEVE avere dimensioni uguali o maggiori della stringa di output, rendendo questo approccio meno desiderabile in alcune circostanze, tutto dipende dal caso d'uso.


0

Se i tuoi valori non sono completamente casuali, ma in realtà possono dipendere da qualcosa - puoi calcolare un hash md5 o sha1 di quel "qualcosa" e poi troncarlo per la lunghezza che desideri.

Inoltre puoi generare e troncare un guid.


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

2
1) i metodi statici dovrebbero essere thread-safe. 2) Qual è il punto di incrementare l'indice invece di utilizzare random.Nextdirettamente il risultato ? Complica il codice e non ottiene nulla di utile.
CodesInChaos

0

Ecco un meccanismo per generare una stringa alfanumerica casuale (la utilizzo per generare password e dati di test) senza definire l'alfabeto e i numeri,

CleanupBase64 rimuoverà le parti necessarie nella stringa e continuerà ad aggiungere lettere alfanumeriche casuali in modo ricorsivo.

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

Hai dichiarato GenerateRandomStringe fai una chiamata GetRandomStringdall'interno SanitiseBase64String. Inoltre si è dichiarato SanitiseBase64String e la chiamata CleanUpBase64Stringin GenerateRandomString.
Wai Ha Lee,

0

C'è uno dei fantastici pacchetti nuget che lo rendono così semplice.

var myObject = new Faker<MyObject>()
.RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/ 7))
.Generate();

Uno dei buoni esempi è qui .


0

non sicuro al 100%, dato che non ho testato OGNI opzione qui, ma di quelle che ho testato, questa è la più veloce. cronometrato con cronometro e ha mostrato 9-10 tick quindi se la velocità è più importante della sicurezza, prova questo:

 private static Random random = new Random(); 
 public static string Random(int length)
     {   
          var stringChars = new char[length];

          for (int i = 0; i < length; i++)
              {
                  stringChars[i] = (char)random.Next(0x30, 0x7a);                  
                  return new string(stringChars);
              }
     }

Perché rispondere A questa domanda è stata data risposta molte volte, eppure pubblichi ancora la tua risposta a metà lavoro ..
Laurent,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.