1324 votes

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

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

2 votes

Quelles sont les restrictions éventuelles concernant le jeu de caractères ? Seulement les caractères de la langue anglaise et 0-9 ? Minuscules et majuscules ?

9 votes

Notez que vous ne devez PAS utiliser de méthode basée sur l'option Random pour générer des mots de passe. L'ensemencement de Random a une entropie très faible, donc ce n'est pas vraiment sûr. Utilisez un PRNG cryptographique pour les mots de passe.

2191voto

dtb Points 104373

J'ai entendu dire que LINQ est le nouveau noir, alors voici ma tentative d'utiliser 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 l'option Random fait de cette classe inapproprié pour tout ce qui concerne la sécurité comme la création de mots de passe ou de jetons. Utilisez le RNGCryptoServiceProvider si vous avez besoin d'un générateur de nombres aléatoires puissant).

0 votes

Nous aurions besoin d'un test de performance sur celui-ci. Peut-être en générant 8000 caractères au lieu de 8.

33 votes

@Alex : J'ai effectué quelques tests rapides et il semble que l'échelle soit assez 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 était presque deux fois plus rapide que celle-ci dans tous les tests.

9 votes

Eh bien. Si votre critère est qu'il utilise linq et qu'il a un code narratif minable, alors c'est définitivement le meilleur. Tant le code narratif que le chemin d'exécution réel sont plutôt inefficaces et indirects. Ne vous méprenez pas, je suis un grand hipster du code (j'adore Python), mais c'est plutôt une machine de Rube Goldberg.

506voto

Dan Rigby Points 5635
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 l'option Random fait de cette classe inapproprié pour tout ce qui concerne la sécurité comme la création de mots de passe ou de jetons. Utilisez le RNGCryptoServiceProvider si vous avez besoin d'un générateur de nombres aléatoires puissant).

6 votes

@Alex : Ce n'est pas la réponse la plus rapide dans l'absolu, mais c'est la réponse "réelle" la plus rapide (c'est-à-dire parmi celles qui permettent de contrôler les caractères utilisés et la longueur de la chaîne).

3 votes

@Alex : Adam Porad's GetRandomFileName est plus rapide mais ne permet pas de contrôler les caractères utilisés et la longueur maximale possible est de 11 caractères. La solution de Douglas Guid est rapide comme l'éclair, mais les caractères sont limités à A-F0-9 et la longueur maximale possible est de 32 caractères.

0 votes

Vous pourriez obtenir plus de 11 caractères en utilisant la solution GetRandomFileName, en obtenant plusieurs chaînes de la méthode GetRandomFileName et en les concaténant. Vous avez raison au sujet du jeu de caractères limité - seulement les minuscules de a à z et les chiffres. Je ne suis pas sûr que les performances soient comparables si vous essayez de générer une longue chaîne aléatoire.

456voto

Eric J. Points 73338

Mise à jour pour .NET 6. RNGCryptoServiceProvider est marqué comme obsolète. À la place, appelez RandomNumberGenerator.Create() . Le code dans la réponse a été mis à jour en conséquence.

MISE À JOUR en fonction des commentaires. L'implémentation originale générait les caractères a-h ~1,95 % du temps et les autres caractères ~1,56 % du temps. La mise à jour génère tous les caractères ~1,61% du temps.

SUPPORT DE RÉSEAU - .NET Core 3 (et les futures plates-formes qui prennent en charge la norme .NET 2.1 ou plus) fournit une méthode cryptographique solide Générateur de nombres aléatoires.GetInt32() pour générer un nombre entier aléatoire dans une plage souhaitée.

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

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 (var crypto = RandomNumberGenerator.Create())
            {
                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();
        }
    }
}

Sur la base d'une discussion des alternatives aquí et mis à jour/modifié sur la base des commentaires ci-dessous.

Voici un petit harnais de test qui démontre la distribution des caractères dans l'ancienne et la nouvelle sortie. Pour une discussion approfondie sur le analyse du caractère aléatoire allez voir sur 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")}%");
            }
        }
    }
}

12 votes

Cette approche me semble correcte : les mots de passe aléatoires, les sels, l'entropie, etc. ne devraient pas être générés à l'aide de Random(), qui est optimisé pour la vitesse et génère des séquences de nombres reproductibles ; RNGCryptoServiceProvider.GetNonZeroBytes(), en revanche, produit des séquences de nombres sauvages qui ne sont PAS reproductibles.

27 votes

Les lettres sont légèrement biaisées (255 % 62 != 0). Malgré ce petit défaut, c'est de loin la meilleure solution ici.

14 votes

Notez que c'est no si vous voulez une force cryptographique, un caractère aléatoire impartial. (Et si vous ne voulez pas cela, alors pourquoi utiliser RNGCSP en premier lieu ?) Utilisation de mod pour indexer dans le chars signifie que vous obtiendrez une sortie biaisée, sauf si chars.Length se trouve être un diviseur de 256.

237voto

Douglas Points 406

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 parce qu'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 un GUID version 6.

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

Solution 2 - Une ligne de code - bonne pour un maximum de 22 caractères

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

Vous ne pouvez pas générer de chaînes de caractères aussi longues que Solution 1 et la chaîne n'a pas la même portée en raison des bits fixes dans les GUID, mais dans de nombreux cas, cela fera l'affaire.

Solution 3 - Un peu moins de code

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

Je garde surtout ceci ici à des fins historiques. Il utilise un peu moins de code, mais au détriment de la portée - parce qu'il utilise l'hexagone au lieu de la base64, il faut plus de caractères pour représenter la même portée par rapport aux autres solutions.

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

22 votes

Vous avez vraiment généré un duplicata ? Surprenant à 5,316,911,983,139,663,491,615,228,241,121,400,000 combinaisons possibles de GUIDs.

2 votes

(La probabilité que cela se produise est de 1,8807909613156600127499784595556e-27, soit pratiquement 0).

75 votes

@Alex : Il raccourcit le GUID à 8 caractères, donc la probabilité de collisions est beaucoup plus élevée que celle des GUID.

99voto

Adam Porad Points 4569

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

Si vous n'avez besoin que de 8 caractères, utilisez Path.GetRandomFileName() dans l'espace de noms System.IO. Sam indique que l'utilisation de la méthode "Path.GetRandomFileName 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 le point (puisqu'il n'est pas aléatoire), puis prendre 8 caractères de la chaîne. En fait, vous pourriez simplement prendre les 8 premiers caractères et ne pas vous soucier du point.

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

PS : merci Sam

42 votes

Cela fonctionne bien. Je l'ai soumis à 100 000 itérations et je n'ai jamais eu de nom en double. Cependant, je a fait trouver plusieurs mots vulgaires (en anglais). Je n'y aurais même pas pensé si l'un des premiers mots de la liste ne contenait pas F***. Je vous préviens juste que si vous utilisez cette fonction pour quelque chose que l'utilisateur verra, vous risquez d'avoir des problèmes.

5 votes

@techturtle Merci pour l'avertissement. Je suppose qu'il y a un risque de mots vulgaires avec toute génération de chaîne aléatoire qui utilise toutes les lettres de l'alphabet.

0 votes

Beau et simple mais pas bon pour les longues chaînes ... votez pour cette bonne astuce

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X