web-dev-qa-db-fra.com

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

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

847
KingNestor

J'ai entendu dire que LINQ était le nouveau noir, voici donc 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 classe Random rend ceci impropre à tout élément lié à la sécurité, comme la création de mots de passe ou de jetons. Utilisez la classe RNGCryptoServiceProvider si vous avez besoin d'un fort aléatoire générateur de nombres.)

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

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

var finalString = new String(stringChars);

Pas aussi élégant que la solution Linq.

(Remarque: l'utilisation de la classe Random rend ceci impropre à tout élément lié à la sécurité, comme la création de mots de passe ou de jetons. Utilisez la classe RNGCryptoServiceProvider si vous avez besoin d'un fort aléatoire générateur de nombres.)

335
Dan Rigby

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

SUPPORT DE CADRE - .NET Core 3 (et les futures plates-formes prenant en charge .NET Standard 2.1 ou ultérieure) fournit une méthode cryptographiquement valable 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 valable .

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 test de harnais qui illustre la répartition des caractères dans l’ancienne sortie mise à jour. Pour une discussion approfondie de analyse du caractère aléatoire , rendez-vous 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")}%");
            }
        }
    }
}
299
Eric J.

Solution 1 - 'gamme' la plus large 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 d'utiliser un GUID, car un GUID a quelques bits fixes qui sont toujours identiques et donc non aléatoires, par exemple le caractère 13 à hexa 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 - convient 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 aussi longtemps que Solution 1 et la chaîne n'a pas la même plage en raison de bits fixes dans les GUID, mais beaucoup des cas cela fera le travail.

Solution 3 - Légèrement moins de code

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

Surtout garder cela ici à des fins historiques. Il utilise un peu moins de code, ce qui implique toutefois moins de plage. Comme il utilise hexadécimal 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 chaînes de 8 caractères a généré un doublon.

190
Douglas

Voici un exemple que j'ai emprunté à Sam Allen, par exemple à Dot Net Perls

Si vous n'avez besoin que de 8 caractères, utilisez Path.GetRandomFileName () dans l'espace de noms System.IO. Sam explique 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 neuvième caractère. Vous devrez donc effacer le point (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 sans 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

61
Adam Porad

Les principaux objectifs de mon code sont les suivants:

  1. La distribution des chaînes est presque uniforme (ne vous souciez pas des déviations mineures, tant qu'elles sont petites)
  2. Il génère plus de quelques milliards de chaînes pour chaque ensemble d'arguments. La génération d'une chaîne de 8 caractères (~ 47 bits d'entropie) n'a pas de 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 (tels que les 62 caractères de la question), cela entraîne un biais négligeable. Les deuxième et troisième propriétés sont obtenues en utilisant RNGCryptoServiceProvider au 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);
}
_
37
CodesInChaos

Le plus simple:

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

Vous pouvez obtenir de meilleures performances si vous codez en dur le tableau de caractères et que vous vous fiez à 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 craignez que les alphabets anglais changent un jour ou l'autre et que vous perdiez du temps, vous pouvez éviter le codage difficile, mais votre performance devrait être légèrement moins bonne (comparable à l'approche Path.GetRandomFileName)

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 l'instance System.Random.

29
nawfal

Quelques comparaisons de performances des différentes réponses de ce fil de discussion:

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]
  • à partir de Select = rsndbztyby [10]
  • from GenerateRandomString = owyefjjakj [10]
  • de SecureFastRandom = VzougLYHYP [10]
  • de SecureFastRandom-NoCache = oVQXNGmO1S [10]

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

  • LinqIsTheNewBlack (10000x) = 96762 ticks écoulés (9,6762 ms)
  • ForLoop (10000x) = 28970 ticks écoulés (2,897 ms)
  • ForLoopNonOptimized (10000x) = 33336 ticks écoulés (3,33336 ms)
  • Répéter (10000x) = 78547 ticks écoulés (7,8547 ms)
  • GenerateRandomString (10000x) = 27416 ticks écoulés (2,77416 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]
21
drzaus

Une ligne de code Membership.GeneratePassword() fait le tour :)

Voici un démo pour le même.

18
Pooran

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 valable.

Faux ... Il y a un biais dans le mot de passe (comme écrit dans un commentaire), bcdefgh sont un peu plus probables que les autres (le a n'est pas parce que par le GetNonZeroBytes il ne génère pas d'octets avec une valeur de zéro, le biais de a est donc contrebalancé), il n'est donc pas vraiment fiable du point de vue de la cryptographie.

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

Question: Pourquoi devrais-je perdre mon temps en utilisant Enumerable.Range au 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 chaînes magiques sont BAD. Est-ce que quelqu'un a 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 ont dit, n'utilisez pas System.Random si vous avez besoin de sécurité cryptographique ...

nb 2: Cette réponse n’est pas la plus efficace ni 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 chaînes magiques que de fournir une réponse novatrice sophistiquée.

6
Wai Ha Lee

Nous utilisons également des chaînes personnalisées aléatoires, mais nous les avons implémentées comme aide d'une 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);
6
Mr. Pumpkin

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

Mon code simple à 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 ceci pour toute 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);
    }
6
Raj kumar

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

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 à partir de: https: //bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.S default (assurez-vous de cliquer sur le bouton Raw pour faciliter la copie et pour vous assurer que vous avez la dernière version, je pense que ce lien renvoie à une version spécifique du code, pas à la dernière).

Notes mises à jour:

  1. Relatif à d'autres réponses - Si vous connaissez la longueur de la sortie, vous n'avez pas besoin d'un StringBuilder. Lorsque vous utilisez ToCharArray, cela crée et remplit le tableau (vous n'avez pas besoin de créer un tableau vide au préalable).
  2. Relatif à d'autres réponses - Vous devriez utiliser NextBytes, plutôt que d'en obtenir une à la fois pour la performance
  3. Techniquement, vous pouvez épingler le tableau d'octets pour un accès plus rapide. Cela vaut généralement la peine lorsque vous effectuez une itération plus de 6 à 8 fois sur un tableau d'octets. (Pas fait ici)
  4. Utilisation de RNGCryptoServiceProvider pour optimiser le caractère aléatoire
  5. Utilisation de la mise en cache d'une mémoire tampon de données aléatoires de 1 Mo - l'analyse comparative montre que la vitesse d'accès des octets mis en cache est environ 1000 fois plus rapide - prenant 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, utilisait un 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 - Premier seul passage = ~ 9-33ms . Imperceptible. en cours : 5 ms (parfois jusqu'à 13 ms) sur 10 000 itérations, avec une seule itération moyenne = 1,5 microseconde. . Remarque: Nécessite généralement deux, mais parfois jusqu'à huit actualisations du cache. Cela dépend du nombre d'octets simples dépassant la zone de polarisation.
  2. Aléatoire - Premier single = ~ 0-1ms . Imperceptible. En cours : 5 ms sur 10 000 itérations. Avec une seule itération moyenne = . 5 microsecondes. . À peu près à la même vitesse.

Également vérifier:

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 supprimer les biais, et de comparer les vitesses et les avantages/inconvénients.

5
Merari Schroeder

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.

5
Rob Deary

Je cherchais une réponse plus spécifique, dans laquelle je voulais contrôler le format de la chaîne aléatoire et tombé sur ce post. Par exemple: les plaques d'immatriculation (des voitures) ont un format spécifique (par pays) et je voulais 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 pouvoir réutiliser le même objet aléatoire, car vous pourriez avoir des doublons dans des scénarios multi-threading). J'ai créé un Gist ( https://Gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), mais je vais également copier 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)];
    }
}
4
Sam Vanhoutte

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);
        }
    }
}
4
james
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
4
Tejas

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 algoritm more secure - change order of letter here

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

        int r;
        var newNumber = string.Empty;

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

        return newNumber;
    }
}

Tests:

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

Maintenant dans la saveur one-liner.

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

Voici une variante de la solution d’Eric J, c’est-à-dire dont la cryptographie est saine, 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 la performance compte (surtout lorsque la longueur est grande):

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

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());
2
w.b

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

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 le même résultat:

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

Certes, il n’est pas optimisé pour la vitesse. Si la mission est de générer des millions de chaînes aléatoires chaque seconde, essayez-en une autre!

REMARQUE: cette solution ne permet pas la répétition de symboles dans l'alphabet. Cet 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.

1
sara

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);
1
Sagar

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

Aussi, vous pouvez générer et tronquer un guid.

0
Alexey B.

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 nombres,

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

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

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

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

    private static int randomSymbolsIndex = 0;

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

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