Comment puis-je générer une chaîne alphanumérique aléatoire de 8 caractères en C# ?
Nous aurions besoin d'un test de performance sur celui-ci. Peut-être en générant 8000 caractères au lieu de 8.
Comment puis-je générer une chaîne alphanumérique aléatoire de 8 caractères en C# ?
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).
Nous aurions besoin d'un test de performance sur celui-ci. Peut-être en générant 8000 caractères au lieu de 8.
@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.
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.
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).
@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).
@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.
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.
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")}%");
}
}
}
}
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.
Les lettres sont légèrement biaisées (255 % 62 != 0). Malgré ce petit défaut, c'est de loin la meilleure solution ici.
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.
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.
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.
(La probabilité que cela se produise est de 1,8807909613156600127499784595556e-27, soit pratiquement 0).
@Alex : Il raccourcit le GUID à 8 caractères, donc la probabilité de collisions est beaucoup plus élevée que celle des GUID.
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
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.
@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.
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.
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 ?
8 votes
Ou peut-être pour stackoverflow.com/questions/730268/ o stackoverflow.com/questions/1122483/c-random-string-generator
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 deRandom
a une entropie très faible, donc ce n'est pas vraiment sûr. Utilisez un PRNG cryptographique pour les mots de passe.2 votes
Ce serait bien d'inclure la localisation de la langue dans cette question. Surtout si votre interface doit s'adapter au chinois ou au bulgare !
0 votes
Voir aussi Comment faire une chaîne aléatoire de chiffres et de lettres d'une longueur de 5 ?
22 votes
Quelque chose avec autant de upvotes et autant de réponses de qualité ne mérite pas d'être marqué comme fermé. Je vote pour qu'il soit réouvert.