Comment puis-je générer des chaînes alphanumériques aléatoires?

968

Comment puis-je générer une chaîne alphanumérique aléatoire de 8 caractères en C #?

KingNestor
la source
2
Quelles restrictions éventuelles avez-vous sur le jeu de caractères? Juste des caractères en anglais et 0-9? Cas mixte?
Eric J.27
9
Notez que vous ne devez PAS utiliser de méthode basée sur la Randomclasse pour générer des mots de passe. L'ensemencement Randoma une entropie très faible, donc ce n'est pas vraiment sûr. Utilisez un PRNG cryptographique pour les mots de passe.
CodesInChaos
2
Ce serait bien d'inclure la localisation linguistique dans cette question. Surtout si votre interface graphique doit prendre en charge le chinois ou le bulgare!
Peter Jamsmenson
15
Quelque chose avec autant de votes positifs et autant de réponses de qualité ne méritait pas d'être marqué comme fermé. Je vote pour sa réouverture.
John Coleman

Réponses:

1686

J'ai entendu que LINQ est le nouveau noir, alors voici ma tentative d'utilisation de 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());
}

(Remarque: L'utilisation de la Randomclasse rend cela inapproprié pour tout ce qui concerne la sécurité , comme la création de mots de passe ou de jetons. Utilisez la RNGCryptoServiceProviderclasse si vous avez besoin d'un générateur de nombres aléatoires fort.)

dtb
la source
24
@Alex: J'ai exécuté quelques tests rapides et il semble évoluer de manière à peu près linéaire lors de la génération de chaînes plus longues (tant qu'il y a suffisamment de mémoire disponible). Cela dit, la réponse de Dan Rigby a été presque deux fois plus rapide que celle-ci à chaque test.
LukeH
6
Bien. Si votre critère est qu'il utilise linq et qu'il a un récit de code moche, c'est certainement les genoux de l'abeille. Le récit du code et le chemin d'exécution réel sont plutôt inefficaces et indirects. Ne vous méprenez pas, je suis un énorme hipster de code (j'adore le python), mais c'est à peu près une machine Rube Goldberg.
eremzeit
5
Bien que cela réponde techniquement à la question, sa sortie est très trompeuse. La génération de 8 caractères aléatoires donne l'impression qu'il peut y avoir de très nombreux résultats, alors qu'au mieux cela produit 2 milliards de résultats différents. Et en pratique encore moins. Vous devez également ajouter un avertissement BIG FAT pour ne pas l'utiliser pour tout ce qui concerne la sécurité.
CodesInChaos
41
@xaisoft: Les lettres minuscules sont laissées comme exercice pour le lecteur.
dtb
15
La ligne suivante est plus efficace en mémoire (et donc en temps) que celle donnéereturn 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);

Pas aussi élégant que la solution Linq.

(Remarque: L'utilisation de la Randomclasse rend cela inapproprié pour tout ce qui concerne la sécurité , comme la création de mots de passe ou de jetons. Utilisez la RNGCryptoServiceProviderclasse si vous avez besoin d'un générateur de nombres aléatoires fort.)

Dan Rigby
la source
4
@Alex: Ce n'est pas la réponse la plus rapide absolue, mais c'est la "vraie" réponse la plus rapide (c'est-à-dire de celles qui permettent de contrôler les caractères utilisés et la longueur de la chaîne).
LukeH
2
@Alex: La GetRandomFileNamesolution d' Adam Porad est plus rapide mais ne permet aucun contrôle des caractères utilisés et la longueur maximale possible est de 11 caractères. La Guidsolution de Douglas est ultra-rapide mais les caractères sont limités à A-F0-9 et la longueur maximale possible est de 32 caractères.
LukeH
1
@Adam: Oui, vous pouvez concaténer le résultat de plusieurs appels à GetRandomFileNamemais (a) vous perdriez votre avantage en termes de performances et (b) votre code deviendrait plus compliqué.
LukeH
2
@xaisoft crée votre instance de l'objet Random () en dehors de votre boucle. Si vous créez de nombreuses instances de Random () dans un court intervalle, l'appel à .Next () renverra la même valeur que Random () utilise une valeur de départ basée sur le temps.
Dan Rigby
2
@xaisoft N'utilisez pas cette réponse pour quoi que ce soit de critique, comme les mots de passe. System.Randomn'est pas adapté à la sécurité.
CodesInChaos
333

MISE À JOUR basée sur les commentaires. L'implémentation d'origine a généré ah ~ 1,95% du temps et les caractères restants ~ 1,56% du temps. La mise à jour génère tous les caractères ~ 1,61% du temps.

PRISE EN CHARGE DU CADRE - .NET Core 3 (et les futures plates-formes prenant en charge .NET Standard 2.1 ou supérieur) fournit une méthode cryptographiquement saine RandomNumberGenerator.GetInt32 () pour générer un entier aléatoire dans une plage souhaitée.

Contrairement à certaines des alternatives présentées, celle-ci est cryptographiquement solide .

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

Basé sur une discussion des alternatives ici et mis à jour / modifié sur la base des commentaires ci-dessous.

Voici un petit harnais de test qui montre la distribution des caractères dans la sortie ancienne et mise à jour. Pour une discussion approfondie de l' analyse du caractère aléatoire , consultez 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")}%");
            }
        }
    }
}
Eric J.
la source
11
Cela ressemble à la bonne approche pour moi - les mots de passe aléatoires, les sels, l'entropie et ainsi de suite ne doivent pas être générés à l'aide de Random () qui est optimisé pour la vitesse et génère des séquences reproductibles de nombres; RNGCryptoServiceProvider.GetNonZeroBytes (), d'autre part, produit des séquences sauvages de nombres qui ne sont PAS reproductibles.
mindplay.dk
25
Les lettres sont légèrement biaisées (255% 62! = 0). Malgré cette faille mineure, c'est de loin la meilleure solution ici.
CodesInChaos
13
Notez que ce n'est pas valable si vous voulez un caractère aléatoire non biaisé et d'une force cryptographique. (Et si vous ne voulez pas cela, alors pourquoi l'utiliser RNGCSPen premier lieu?) L'utilisation de mod pour indexer dans le charstableau signifie que vous obtiendrez une sortie biaisée à moins chars.Lengthqu'il ne soit un diviseur de 256.
LukeH
15
Une possibilité de réduire considérablement le biais consiste à demander 4*maxSizedes octets aléatoires, puis à les utiliser (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. J'utiliserais également GetBytesau lieu de GetNonZeroBytes. Et enfin, vous pouvez supprimer le premier appel à GetNonZeroBytes. Vous n'utilisez pas son résultat.
CodesInChaos
14
Fait amusant: AZ az 0-9 est de 62 caractères. Les gens soulignent le biais des lettres parce que 256% 62! = 0. Les identifiants vidéo de YouTube sont AZ az 0-9, ainsi que "-" et "_", qui produisent 64 caractères possibles, qui se divisent en 256 uniformément. Coïncidence? Je crois que non! :)
qJake
200

Solution 1 - la plus grande «gamme» avec la longueur la plus flexible

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

Cette solution a plus de portée que l'utilisation d'un GUID car un GUID a quelques bits fixes qui sont toujours les mêmes et donc non aléatoires, par exemple le caractère 13 en hexadécimal est toujours "4" - au moins dans une version 6 GUID.

Cette solution vous permet également de générer une chaîne de n'importe quelle longueur.

Solution 2 - Une ligne de code - valable jusqu'à 22 caractères

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

Vous ne pouvez pas générer de chaînes tant que la solution 1 et la chaîne n'ont pas la même plage en raison de bits fixes dans les GUID, mais dans de nombreux cas, cela fera l'affaire.

Solution 3 - Légèrement moins de code

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

Surtout en gardant cela ici à des fins historiques. Il utilise un peu moins de code, mais cela vient au détriment d'avoir moins de portée - car il utilise hex au lieu de base64, il faut plus de caractères pour représenter la même plage par rapport aux autres solutions.

Ce qui signifie plus de chances de collision - le tester avec 100 000 itérations de 8 chaînes de caractères a généré un doublon.

Douglas
la source
22
Vous avez en fait généré un doublon? Surprenant à 5,316,911,983,139,663,491,615,228,241,121,400,000 combinaisons possibles de GUID.
Alex
73
@Alex: Il raccourcit le GUID à 8 caractères, donc la probabilité de collisions est beaucoup plus élevée que celle des GUID.
dtb
23
Personne ne peut l'apprécier à part les nerds :) Oui, vous avez absolument raison, la limite de 8 caractères fait la différence.
Alex
31
Guid.NewGuid (). ToString ("n") empêchera les tirets, aucun appel Replace () n'est nécessaire. Mais il faut le mentionner, les GUID ne sont que 0-9 et AF. Le nombre de combinaisons est «assez bon», mais loin de ce que permet une vraie chaîne aléatoire alphanumérique. Les risques de collision sont de 1: 4 294 967 296 - les mêmes qu'un entier aléatoire de 32 bits.
richardtallent
32
1) Les GUID sont conçus pour être uniques et non aléatoires. Bien que les versions actuelles de Windows génèrent des GUID V4 qui sont en effet aléatoires, ce n'est pas garanti. Par exemple, les anciennes versions de Windows utilisaient des GUID V1, où votre pourrait échouer. 2) Le simple fait d'utiliser des caractères hexadécimaux réduit considérablement la qualité de la chaîne aléatoire. De 47 à 32 bits. 3) Les gens sous-estiment la probabilité de collision, car ils la donnent pour des paires individuelles. Si vous générez des valeurs de 100k 32 bits, vous avez probablement une collision entre elles. Voir problème d'anniversaire.
CodesInChaos
72

Voici un exemple que j'ai volé à l'exemple de Sam Allen chez Dot Net Perls

Si vous n'avez besoin que de 8 caractères, utilisez Path.GetRandomFileName () dans l'espace de noms System.IO. Sam dit que l'utilisation de la "méthode Path.GetRandomFileName ici est parfois supérieure, car elle utilise RNGCryptoServiceProvider pour un meilleur caractère aléatoire. Cependant, elle est limitée à 11 caractères aléatoires."

GetRandomFileName renvoie toujours une chaîne de 12 caractères avec un point au 9ème caractère. Vous devrez donc supprimer la période (car ce n'est pas aléatoire), puis prendre 8 caractères de la chaîne. En fait, vous pouvez simplement prendre les 8 premiers caractères et ne pas vous soucier de la période.

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

PS: merci Sam

Adam Porad
la source
27
Cela fonctionne bien. Je l'ai parcouru 100 000 itérations et je n'ai jamais eu de nom en double. Cependant, je ne trouve plusieurs mots vulgaires (en anglais). N'aurait même pas pensé à ça, sauf que l'un des premiers de la liste avait F *** dedans. Juste un avertissement si vous l'utilisez pour quelque chose que l'utilisateur verra.
techturtle
3
@techturtle Merci pour l'avertissement. Je suppose qu'il y a un risque pour les mots vulgaires avec toute génération de chaînes aléatoires qui utilise toutes les lettres de l'alphabet.
Adam Porad
agréable et simple mais pas bon pour une longue corde ... votez pour ce bon truc
Maher Abuthraa
Cette méthode semble renvoyer uniquement des chaînes alphanumériques en minuscules.
jaybro
2
Il y a des mots vulgaires de temps en temps, mais si vous continuez à le faire assez longtemps, il écrit finalement Shakespeare. (Juste quelques vies de l'univers. :)
Slothario
38

Les principaux objectifs de mon code sont:

  1. La distribution des cordes est presque uniforme (ne vous souciez pas des écarts mineurs, tant qu'ils sont petits)
  2. Il génère plus de quelques milliards de chaînes pour chaque ensemble d'arguments. Générer une chaîne de 8 caractères (~ 47 bits d'entropie) n'a aucun sens si votre PRNG ne génère que 2 milliards (31 bits d'entropie) de valeurs différentes.
  3. C'est sécurisé, car je m'attends à ce que les gens l'utilisent pour les mots de passe ou autres jetons de sécurité.

La première propriété est obtenue en prenant une valeur de 64 bits modulo la taille de l'alphabet. Pour les petits alphabets (comme les 62 caractères de la question), cela conduit à un biais négligeable. Les deuxième et troisième propriétés sont obtenues en utilisant RNGCryptoServiceProviderau lieu de 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);
}
CodesInChaos
la source
1
Il n'y a pas d'intersection avec 64 x Z et Math.Pow (2, Y). Ainsi, tout en augmentant le nombre réduit le biais, il ne l'élimine pas. J'ai mis à jour ma réponse ci-dessous, mon approche était de rejeter les entrées aléatoires et de les remplacer par une autre valeur.
Todd
@Todd Je sais que cela n'élimine pas le biais, mais j'ai choisi la simplicité de cette solution plutôt que d'éliminer un biais pratiquement hors de propos.
CodesInChaos
Je suis d'accord pour la plupart des cas, c'est probablement pratiquement hors de propos. Mais maintenant, j'ai mis à jour le mien pour être à la fois aussi rapide que Random et un peu plus sécurisé que le vôtre. Tous open source pour tous à partager. Oui, j'ai perdu trop de temps à ce sujet ...
Todd
Si nous utilisons le fournisseur RNG, avons-nous un moyen d'éviter les biais en théorie? Je ne suis pas sûr ... Si Todd veut dire le chemin quand il génère un nombre aléatoire supplémentaire (quand nous sommes dans la zone de biais) alors cela peut être une hypothèse erronée. Le RNG a une distribution presque linéaire de toutes les valeurs générées en moyenne. Mais cela ne signifie pas que nous n'aurons pas de corrélation locale entre les octets générés. Donc, l'octet supplémentaire uniquement pour la zone de biais peut encore nous donner un certain biais, mais pour une raison différente. Ce biais sera très probablement très faible. MAIS dans ce cas, l'augmentation du nombre total d'octets générés est plus simple.
Maxim
1
@Maxim Vous pouvez utiliser le rejet pour éliminer complètement le biais (en supposant que le générateur sous-jacent est parfaitement aléatoire). En échange, le code peut durer arbitrairement (avec une probabilité exponentiellement faible).
CodesInChaos
32

Le plus simple:

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

Vous pouvez obtenir de meilleures performances en codant en dur le tableau char et en vous appuyant sur System.Random:

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

Si jamais vous vous inquiétez, les alphabets anglais peuvent changer quelque temps et vous pourriez perdre des affaires, alors vous pouvez éviter le codage en dur, mais devrait fonctionner légèrement moins bien (comparable à l' Path.GetRandomFileNameapproche)

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

Les deux dernières approches semblent meilleures si vous pouvez en faire une méthode d'extension sur System.Randominstance.

nawfal
la source
1
L'utilisation chars.Selectest très moche car elle repose sur la taille de sortie étant au plus la taille de l'alphabet.
CodesInChaos
@CodesInChaos Je ne sais pas si je vous comprends. Tu veux dire dans l' 'a'.To('z')approche?
nawfal
1
1) chars.Select().Take (n) `ne fonctionne que si chars.Count >= n. Sélectionner une séquence que vous n'utilisez pas réellement n'est pas intuitif, surtout avec cette contrainte de longueur implicite. Je préfère utiliser Enumerable.Rangeou Enumerable.Repeat. 2) Le message d'erreur "le caractère de fin doit être inférieur au caractère de début" est dans le mauvais sens / manque a not.
CodesInChaos
@CodesInChaos mais dans mon cas, chars.Countc'est moyen > n. Je n'ai pas non plus la partie non intuitive. Cela rend toutes les utilisations non Takeintuitives, n'est-ce pas? Je n'y crois pas. Merci d'avoir pointé la faute de frappe.
nawfal
4
Ceci est présenté sur theDailyWTF.com en tant qu'article CodeSOD.
22

Juste quelques comparaisons de performances des différentes réponses de ce fil:

Méthodes et configuration

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

Résultats

Testé dans LinqPad. Pour une taille de chaîne de 10, génère:

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

Et les performances ont tendance à varier légèrement, très occasionnellement NonOptimizedest en fait plus rapide, et parfois ForLoopet GenerateRandomStringchanger qui est en tête.

  • LinqIsTheNewBlack (10000x) = 96762 ticks écoulés (9,6762 ms)
  • ForLoop (10000x) = 28970 ticks écoulés (2,8897 ms)
  • ForLoopNonOptimized (10000x) = 33336 ticks écoulés (3,33336 ms)
  • Répétition (10000x) = 78547 ticks écoulés (7.8547 ms)
  • GenerateRandomString (10000x) = 27416 ticks écoulés (2,7416 ms)
  • SecureFastRandom (10000x) = 13176 ticks écoulés (5 ms) le plus bas [Machine différente]
  • SecureFastRandom-NoCache (10000x) = 39541 ticks écoulés (17 ms) le plus bas [Machine différente]
drzaus
la source
3
Il serait intéressant de savoir lesquels ont créé des dupes.
Rebecca
@Junto - pour déterminer ce qui entraîne des doublons, quelque chose comme var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, où vous remplacez EachRandomizingMethodpar ... chaque méthode
drzaus
19

Une ligne de code Membership.GeneratePassword()fait l'affaire :)

Voici une démo pour le même.

Pooran
la source
1
Il semble que Microsoft ait déplacé le lien. Un autre exemple de code se trouve sur msdn.microsoft.com/en-us/library/ms152017 ou aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx ou developer.xamarin.com/api/member/…
Pooran
J'y ai pensé mais je n'ai pas pu me débarrasser des caractères non alphanumériques car le deuxième argument est le nombre MINIMUM de caractères non alpha
ozzy432836
13

Le code écrit par Eric J. est assez bâclé (il est clair qu'il date d'il y a 6 ans ... il n'écrirait probablement pas ce code aujourd'hui), et il y a même des problèmes.

Contrairement à certaines des alternatives présentées, celle-ci est cryptographiquement solide.

Faux ... Il y a un biais dans le mot de passe (tel qu'écrit dans un commentaire), bcdefghsont un peu plus probables que les autres (ce an'est pas parce GetNonZeroBytesqu'il ne génère pas d'octets avec une valeur de zéro, donc le biais car il aest équilibré par lui), donc il n'est pas vraiment cryptographiquement solide.

Cela devrait corriger tous les problèmes.

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);
    }
}
xanatos
la source
7

Nous utilisons également une chaîne personnalisée aléatoire, mais nous l'avons implémentée comme une aide de chaîne, ce qui offre une certaine flexibilité ...

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

Usage

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

ou

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
Mr. Pumpkin
la source
7

Mon code simple d'une ligne fonctionne pour moi :)

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

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

Pour développer cela pour n'importe quelle chaîne de longueur

    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);
    }
Raj kumar
la source
J'aime aussi la méthode guid - se sent vraiment légère
ozzy432836
6

Une autre option pourrait être d'utiliser Linq et d'agréger des caractères aléatoires dans un constructeur de chaînes.

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());
AAD
la source
6

Question: Pourquoi devrais-je perdre mon temps à utiliser Enumerable.Rangeau lieu de taper "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;
    }
}

Réponse: les cordes magiques sont MAUVAISES. Quelqu'un a-t-il remarqué qu'il n'y avait pas de " I" dans ma chaîne en haut? Ma mère m'a appris à ne pas utiliser de cordes magiques pour cette raison même ...

nb 1: Comme beaucoup d'autres comme @dtb l'ont dit, ne l'utilisez pas System.Randomsi vous avez besoin d'une sécurité cryptographique ...

nb 2: Cette réponse n'est pas la plus efficace ou la plus courte, mais je voulais que l'espace sépare la réponse de la question. Le but de ma réponse est plus de mettre en garde contre les cordes magiques que de fournir une réponse innovante de fantaisie.

Wai Ha Lee
la source
Pourquoi est-ce que je me soucie qu'il n'y ait pas de " I?"
Christine
1
Alphanumérique (en ignorant la casse) est [A-Z0-9]. Si, par accident, votre chaîne aléatoire ne couvre que [A-HJ-Z0-9]le résultat, elle ne couvre pas toute la plage autorisée, ce qui peut être problématique.
Wai Ha Lee
En quoi cela serait-il problématique? Donc ça ne contient pas I. Est-ce parce qu'il y a un caractère de moins et que cela facilite le crack? Quelles sont les statistiques sur les mots de passe craquables qui contiennent 35 caractères dans la plage plutôt que 36. Je pense que je préfère le risquer ... ou simplement vérifier la plage de caractères ... plutôt que d'inclure toutes ces ordures supplémentaires dans mon code. Mais c'est moi. Je veux dire, ne pas être un trou de cul, je dis juste. Parfois, je pense que les programmeurs ont tendance à emprunter la voie extra-complexe pour être extra-complexes.
Christine
1
Il approfondit le cas d'utilisation. Il est très courant d'exclure des caractères tels que Iet Ode ces types de chaînes aléatoires pour éviter que les humains ne les confondent avec 1et 0. Si vous ne vous souciez pas d'avoir une chaîne lisible par l'homme, très bien, mais si c'est quelque chose que quelqu'un pourrait avoir besoin de taper, il est en fait intelligent de supprimer ces caractères.
Chris Pratt
5

Une version légèrement plus propre de la solution de 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);

Vos préférences de style peuvent varier.

Rob Deary
la source
C'est beaucoup mieux et plus efficace que la réponse acceptée.
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());
    }
Tejas
la source
5

Après avoir examiné les autres réponses et pris en compte les commentaires de CodeInChaos, ainsi que la réponse de CodeInChaos toujours biaisée (bien que moins), j'ai pensé qu'une solution finale de couper-coller était nécessaire. Donc, tout en mettant à jour ma réponse, j'ai décidé de tout faire.

Pour une version à jour de ce code, veuillez visiter le nouveau référentiel Hg sur Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Je vous recommande de copier et coller le code de: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (assurez - vous que vous cliquez sur le bouton Raw pour faciliter la copie et vous assurer que vous avez la dernière version, je pense que ce lien va vers une version spécifique du code, pas la dernière).

Notes mises à jour:

  1. Concernant certaines autres réponses - Si vous connaissez la longueur de la sortie, vous n'avez pas besoin d'un StringBuilder, et lorsque vous utilisez ToCharArray, cela crée et remplit le tableau (vous n'avez pas besoin de créer d'abord un tableau vide)
  2. Concernant d'autres réponses - Vous devez utiliser NextBytes, plutôt que d'en obtenir une à la fois pour les performances
  3. Techniquement, vous pouvez épingler le tableau d'octets pour un accès plus rapide .. cela en vaut généralement la peine lorsque vous itérez plus de 6-8 fois sur un tableau d'octets. (Pas fait ici)
  4. Utilisation de RNGCryptoServiceProvider pour le meilleur caractère aléatoire
  5. Utilisation de la mise en cache d'un tampon de 1 Mo de données aléatoires - l'analyse comparative montre que la vitesse d'accès en octets uniques mis en cache est ~ 1000 fois plus rapide - ce qui prend 9 ms sur 1 Mo contre 989 ms pour la mise en cache.
  6. Rejet optimisé de la zone de biais au sein de ma nouvelle classe.

Solution finale à la question:

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

Mais vous avez besoin de ma nouvelle classe (non testée):

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

Pour l'histoire - mon ancienne solution pour cette réponse, utilisé objet aléatoire:

    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 - Première exécution unique = ~ 9-33 ms . Imperceptible. En cours : 5 ms (parfois jusqu'à 13 ms) sur 10 000 itérations, avec une seule itération moyenne = 1,5 microsecondes. . Remarque: nécessite généralement 2, mais parfois jusqu'à 8 rafraîchissements de cache - dépend du nombre d'octets uniques dépassant la zone de polarisation
  2. Aléatoire - Premier cycle unique = ~ 0-1 ms . Imperceptible. En cours : 5 ms sur 10 000 itérations. Avec une seule itération moyenne = 0,5 microsecondes. . À peu près à la même vitesse.

Consultez également:

Ces liens sont une autre approche. La mise en mémoire tampon pourrait être ajoutée à cette nouvelle base de code, mais le plus important était d'explorer différentes approches pour éliminer les biais et de comparer les vitesses et les avantages / inconvénients.

Merari Schroeder
la source
J'ai trouvé de légères améliorations de performances à votre méthode, ce qui semblait être le jeûne du groupe - stackoverflow.com/a/17092645/1037948
drzaus
5
1) Pourquoi toutes ces constantes magiques? Vous avez spécifié la longueur de sortie trois fois. Il suffit de le définir comme une constante ou un paramètre. Vous pouvez utiliser à la charSet.Lengthplace de 62. 2) Un statique Randomsans verrouillage signifie que ce code n'est pas threadsafe. 3) la réduction de 0-255 mod 62 introduit une polarisation détectable. 4) Vous ne pouvez pas utiliser ToStringsur un tableau de caractères, qui revient toujours "System.Char[]". Vous devez utiliser à la new String(rName)place.
CodesInChaos
Merci @CodesInChaos, je n'avais jamais pensé à ces choses à l'époque. Toujours en utilisant uniquement la classe Random, mais cela devrait être mieux. Je ne pouvais pas penser à une meilleure façon de détecter et de corriger les entrées de polarisation.
Todd
C'est un peu idiot de commencer avec un RNG faible ( System.Random) puis d'éviter soigneusement tout biais dans votre propre code. L'expression "polir un étron" vient à l'esprit.
CodesInChaos
@CodesInChaos Et maintenant l'apprenti a dépassé son maître
Todd
4

Horrible, je sais, mais je ne pouvais pas m'en empêcher:


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

James
la source
4

Je cherchais une réponse plus spécifique, où je veux contrôler le format de la chaîne aléatoire et suis tombé sur ce post. Par exemple: les plaques d'immatriculation (des voitures) ont un format spécifique (par pays) et j'ai voulu créer des plaques d'immatriculation aléatoires.
J'ai décidé d'écrire ma propre méthode d'extension de Random pour cela. (ceci afin de réutiliser le même objet Random, car vous pourriez avoir des doublons dans des scénarios multi-thread). J'ai créé un résumé ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), mais je copierai également la classe d'extension ici:

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)];
    }
}
Sam Vanhoutte
la source
4

Maintenant dans la saveur d'une doublure.

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());
}
Matas Vaitkevicius
la source
2
L'utilisation d'une propriété pour quelque chose qui change à chaque accès est plutôt douteuse. Je recommanderais plutôt d'utiliser une méthode.
CodesInChaos
2
RNGCryptoServiceProviderdoit être jeté après utilisation.
Tsahi Asher
J'ai résolu le problème IDisposable, mais cela reste très douteux, créant un nouveau RNGCryptoServiceProvider pour chaque lettre.
piojo
@CodesInChaos fait, maintenant une méthode.
Matas Vaitkevicius
3

Essayez de combiner deux parties: unique (séquence, compteur ou date) et aléatoire

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

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())
    }
RouR
la source
1) Vous pouvez utiliser des littéraux de caractères au lieu des valeurs ASCII associées à ces caractères. 2) Vous avez une erreur ponctuelle dans votre code de correspondance d'intervalle. Vous devez utiliser <=et >=au lieu de <et >. 3) J'ajouterais les parenthèses inutiles autour des &&expressions pour préciser qu'elles ont la priorité, mais bien sûr, ce n'est qu'un choix stylistique.
CodesInChaos
+ 1 Bon pour supprimer les biais et ajouter des tests. Je ne sais pas pourquoi vous ajoutez votre chaîne aléatoire à une chaîne dérivée de l'horodatage? En outre, vous avez encore besoin de disposer de votre RNGCryptoServiceProvider
monty
2

Une solution sans utiliser Random:

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

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
wb
la source
2
NewGuid utilise au hasard en interne. Donc, cela utilise toujours au hasard, c'est juste le cacher.
Wedge
2

Voici une variante de la solution d'Eric J, c'est à dire cryptographiquement solide, pour 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();
}

Si les performances sont importantes (en particulier lorsque la longueur est élevée):

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();
}
huyc
la source
1
Ce n'est pas cryptographiquement solide. Il y a un petit biais dû au fait que l'opération de module ne répartit pas également toute la largeur de ulong en 62 caractères.
Lie Ryan
1

Je sais que celui-ci n'est pas le meilleur moyen. Mais vous pouvez essayer ça.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
Sagar
la source
2
Comment est cette ligne? Console.WriteLine ($ "Chaîne aléatoire: {Path.GetRandomFileName (). Replace (". "," ")}"); est une ligne.
PmanAce
1

Je ne sais pas à quel point c'est cryptographique, mais c'est plus lisible et concis que les solutions les plus complexes de loin (imo), et cela devrait être plus "aléatoire" que les System.Randomsolutions basées sur.

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

Je ne peux pas décider si je pense que cette version ou la suivante est "plus jolie", mais elles donnent exactement les mêmes résultats:

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

Certes, il n'est pas optimisé pour la vitesse, donc s'il est essentiel à la mission de générer des millions de chaînes aléatoires chaque seconde, essayez-en une autre!

REMARQUE: Cette solution ne permet pas de répétitions de symboles dans l'alphabet, et l'alphabet DOIT être de taille égale ou supérieure à la chaîne de sortie, ce qui rend cette approche moins souhaitable dans certaines circonstances, tout dépend de votre cas d'utilisation.

Sara
la source
0

Si vos valeurs ne sont pas complètement aléatoires, mais peuvent en fait dépendre de quelque chose - vous pouvez calculer un hachage md5 ou sha1 de ce `` somwthing '', puis le tronquer à la longueur souhaitée.

Vous pouvez également générer et tronquer un guid.

Alexey B.
la source
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;
    }
}
KregHEk
la source
2
1) les méthodes statiques doivent être thread-safe. 2) Quel est l'intérêt d'incrémenter l'index au lieu d'utiliser random.Nextdirectement le résultat de ? Complique le code et n'obtient rien d'utile.
CodesInChaos
0

Voici un mécanisme pour générer une chaîne alphanumérique aléatoire (je l'utilise pour générer des mots de passe et des données de test) sans définir l'alphabet et les chiffres,

CleanupBase64 supprimera les parties nécessaires de la chaîne et continuera à ajouter récursivement des lettres alphanumériques aléatoires.

        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);
        }
Dhanuka777
la source
Vous avez déclaré GenerateRandomStringet appelé GetRandomStringde l'intérieur SanitiseBase64String. Aussi , vous avez déclaré SanitiseBase64String et appel CleanUpBase64Stringà GenerateRandomString.
Wai Ha Lee
0

Il y a l'un des superbes paquets de pépites qui rendent cela si simple.

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

Un des bons exemples est ici .

nzrytmn
la source
0

pas sûr à 100%, car je n'ai pas testé CHAQUE option ici, mais parmi celles que j'ai testées, celle-ci est la plus rapide. chronométré avec chronomètre et il a montré 9-10 ticks donc si la vitesse est plus importante que la sécurité, essayez ceci:

 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);
              }
     }
PetNoire
la source
Pourquoi répondre? Cette question a reçu de nombreuses réponses et pourtant vous postez toujours votre réponse semi-professionnelle ..
Laurent