¿Cómo puedo generar cadenas alfanuméricas aleatorias?


968

¿Cómo puedo generar una cadena alfanumérica aleatoria de 8 caracteres en C #?


2
¿Qué restricciones, si hay alguna, tiene en el conjunto de caracteres? ¿Solo caracteres en inglés y 0-9? ¿Caso mixto?
Eric J.


99
Tenga en cuenta que NO debe usar ningún método basado en la Randomclase para generar contraseñas. La siembra de Randomtiene una entropía muy baja, por lo que no es realmente segura. Use un PRNG criptográfico para las contraseñas.
CodesInChaos

2
Sería bueno incluir la localización del idioma en esta pregunta. ¡Especialmente si su interfaz gráfica de usuario necesita atender chino o búlgaro!
Peter Jamsmenson el

15
Algo con tantos votos a favor y tantas respuestas de calidad no merece ser marcado como cerrado. Voto para que se vuelva a abrir.
John Coleman

Respuestas:


1686

Escuché que LINQ es el nuevo negro, así que aquí está mi intento de usar LINQ:

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

(Nota: el uso de la Randomclase hace que esto sea inadecuado para cualquier cosa relacionada con la seguridad , como crear contraseñas o tokens. Use la RNGCryptoServiceProviderclase si necesita un generador de números aleatorios seguro).


24
@ Alex: He realizado algunas pruebas rápidas y parece escalar de forma bastante lineal cuando se generan cadenas más largas (siempre que haya suficiente memoria disponible). Dicho esto, la respuesta de Dan Rigby fue casi el doble de rápida que esta en cada prueba.
LukeH

66
Bien. Si su criterio es que usa linq y que tiene una narrativa de código pésima, entonces definitivamente son las rodillas de la abeja. Tanto la narrativa del código como la ruta de ejecución real son bastante ineficientes e indirectas. No me malinterpreten, soy un gran inconformista de código (me encanta Python), pero esto es más o menos una máquina de Rube Goldberg.
eremzeit

55
Si bien esto técnicamente responde a la pregunta, su salida es muy engañosa. La generación de sonidos 8 caracteres aleatorios como no puede haber muy muchos resultados, mientras que esto produce en el mejor de 2 mil millones de resultados diferentes. Y en la práctica aún menos. También debe agregar una advertencia GRANDE GRASA para no usar esto para nada relacionado con la seguridad.
CodesInChaos

41
@xaisoft: las letras minúsculas se dejan como ejercicio para el lector.
dtb

15
La siguiente línea es más eficiente en memoria (y, por lo tanto, tiempo) que la dadareturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Tyson Williams el

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

No es tan elegante como la solución Linq.

(Nota: el uso de la Randomclase hace que esto sea inadecuado para cualquier cosa relacionada con la seguridad , como crear contraseñas o tokens. Use la RNGCryptoServiceProviderclase si necesita un generador de números aleatorios seguro).


44
@Alex: Esta no es la respuesta más rápida absoluta, pero es la respuesta "real" más rápida (es decir, de aquellas que permiten el control sobre los caracteres utilizados y la longitud de la cadena).
LukeH

2
@Alex: la GetRandomFileNamesolución de Adam Porad es más rápida pero no permite ningún control de los caracteres utilizados y la longitud máxima posible es de 11 caracteres. La Guidsolución de Douglas es ultrarrápida, pero los caracteres están restringidos a A-F0-9 y la longitud máxima posible es de 32 caracteres.
LukeH

1
@ Adam: Sí, podría concatenar el resultado de múltiples llamadas a, GetRandomFileNamepero luego (a) perdería su ventaja de rendimiento y (b) su código se volvería más complicado.
LukeH

2
@xaisoft crea tu instancia del objeto Random () fuera de tu bucle. Si crea muchas instancias de Random () en un intervalo corto, la llamada a .Next () devolverá el mismo valor que Random () utiliza una semilla basada en el tiempo.
Dan Rigby

2
@xaisoft No use esta respuesta para nada crítico para la seguridad, como contraseñas. System.RandomNo es adecuado para la seguridad.
CodesInChaos

333

ACTUALIZADO según los comentarios. La implementación original generó ah ~ 1.95% del tiempo y los caracteres restantes ~ 1.56% del tiempo. La actualización genera todos los caracteres ~ 1.61% del tiempo.

SOPORTE DE MARCO: .NET Core 3 (y futuras plataformas que admitan .NET Standard 2.1 o superior) proporciona un método criptográficamente sólido RandomNumberGenerator.GetInt32 () para generar un entero aleatorio dentro de un rango deseado.

A diferencia de algunas de las alternativas presentadas, esta es criptográficamente sólida .

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

Basado en una discusión de alternativas aquí y actualizado / modificado según los comentarios a continuación.

Aquí hay un pequeño arnés de prueba que demuestra la distribución de caracteres en la salida anterior y actualizada. Para una discusión profunda del análisis de aleatoriedad , visite random.org.

using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;

namespace CryptoRNGDemo
{
    class Program
    {

        const int REPETITIONS = 1000000;
        const int KEY_SIZE = 32;

        static void Main(string[] args)
        {
            Console.WriteLine("Original BIASED implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);

            Console.WriteLine("Updated implementation");
            PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
            Console.ReadKey();
        }

        static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
        {
            Dictionary<char, int> counts = new Dictionary<char, int>();
            foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);

            for (int i = 0; i < REPETITIONS; i++)
            {
                var key = generator(KEY_SIZE); 
                foreach (var ch in key) counts[ch]++;
            }

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}

11
Este parece ser el enfoque correcto para mí: las contraseñas aleatorias, las sales, la entropía, etc. no deben generarse utilizando Random (), que está optimizado para la velocidad y genera secuencias reproducibles de números; RNGCryptoServiceProvider.GetNonZeroBytes () por otro lado produce secuencias salvajes de números que NO son reproducibles.
mindplay.dk

25
Las letras están ligeramente sesgadas (255% 62! = 0). A pesar de este pequeño defecto, es la mejor solución aquí.
CodesInChaos

13
Tenga en cuenta que esto no es correcto si desea una fuerza criptográfica, aleatoriedad imparcial. (Y si no quieres eso, ¿por qué usarlo RNGCSPen primer lugar?) Usar mod para indexar en la charsmatriz significa que obtendrás una salida sesgada a menos chars.Lengthque sea un divisor de 256.
LukeH

15
Una posibilidad para reducir mucho el sesgo es solicitar 4*maxSizebytes aleatorios y luego usarlos (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. También lo usaría en GetByteslugar de GetNonZeroBytes. Y finalmente puedes eliminar la primera llamada a GetNonZeroBytes. No estás usando su resultado.
CodesInChaos

14
Dato curioso: AZ az 0-9 tiene 62 caracteres. La gente señala el sesgo de las letras porque 256% 62! = 0. Las identificaciones de video de YouTube son AZ az 0-9, así como "-" y "_", que producen 64 caracteres posibles, que se dividen en 256 de manera uniforme. ¿Coincidencia? ¡Yo creo que no! :)
qJake

200

Solución 1: el 'rango' más grande con la longitud más 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);
    }
}

Esta solución tiene más alcance que usar un GUID porque un GUID tiene un par de bits fijos que siempre son iguales y, por lo tanto, no aleatorios, por ejemplo, el carácter de 13 en hexadecimal siempre es "4", al menos en un GUID de la versión 6.

Esta solución también le permite generar una cadena de cualquier longitud.

Solución 2: una línea de código, válida para hasta 22 caracteres

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

No puede generar cadenas siempre que la Solución 1 y la cadena no tengan el mismo rango debido a bits fijos en los GUID, pero en muchos casos esto hará el trabajo.

Solución 3: un poco menos de código

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

Principalmente manteniendo esto aquí para fines históricos. Utiliza un poco menos de código, lo que sin embargo implica el gasto de tener menos rango, ya que usa hexadecimal en lugar de base64, se necesitan más caracteres para representar el mismo rango en comparación con las otras soluciones.

Lo que significa más posibilidades de colisión: probarlo con 100,000 iteraciones de 8 cadenas de caracteres generó un duplicado.


22
¿Realmente generaste un duplicado? Sorprendente en 5,316,911,983,139,663,491,615,228,241,121,400,000 posibles combinaciones de GUID.
Alex

73
@Alex: está acortando el GUID a 8 caracteres, por lo que la probabilidad de colisiones es mucho mayor que la de los GUID.
dtb

23
Nadie puede apreciar esto aparte de los nerds :) Sí, tienes toda la razón, el límite de 8 caracteres hace la diferencia.
Alex

31
Guid.NewGuid (). ToString ("n") mantendrá los guiones, no se necesita la llamada Reemplazar (). Pero debe mencionarse, los GUID son solo 0-9 y AF. El número de combinaciones es "suficientemente bueno", pero no se acerca a lo que permite una cadena aleatoria alfanumérica verdadera . Las posibilidades de colisión son 1: 4,294,967,296, lo mismo que un entero aleatorio de 32 bits.
richardtallent

32
1) Los GUID están diseñados para ser únicos, no aleatorios. Si bien las versiones actuales de Windows generan GUID V4 que de hecho son aleatorios, eso no está garantizado. Por ejemplo, las versiones anteriores de Windows usaban GUID V1, donde su error podría fallar. 2) Solo usar caracteres hexadecimales reduce significativamente la calidad de la cadena aleatoria. De 47 a 32 bits. 3) Las personas están subestimando la probabilidad de colisión, ya que la dan para parejas individuales. Si genera 100k valores de 32 bits, probablemente tenga una colisión entre ellos. Ver problema de cumpleaños.
CodesInChaos

72

Aquí hay un ejemplo que robé del ejemplo de Sam Allen en Dot Net Perls

Si solo necesita 8 caracteres, use Path.GetRandomFileName () en el espacio de nombres System.IO. Sam dice que usar el método "Path.GetRandomFileName aquí es a veces superior, porque usa RNGCryptoServiceProvider para una mejor aleatoriedad. Sin embargo, está limitado a 11 caracteres aleatorios".

GetRandomFileName siempre devuelve una cadena de 12 caracteres con un punto en el noveno carácter. Por lo tanto, deberá eliminar el punto (ya que no es aleatorio) y luego tomar 8 caracteres de la cadena. En realidad, puedes tomar los primeros 8 caracteres y no preocuparte por el período.

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

PD: gracias Sam


27
Esto funciona bien Lo ejecuté a través de 100,000 iteraciones y nunca tuve un nombre duplicado. Sin embargo, yo me encuentro varias palabras vulgares (en Inglés). Ni siquiera habría pensado en esto, excepto que uno de los primeros en la lista tenía F ***. Solo un aviso si usa esto para algo que el usuario verá.
Techturtle

3
@techturtle Gracias por la advertencia. Supongo que existe el riesgo de palabras vulgares con cualquier generación de cadenas al azar que use todas las letras del alfabeto.
Adam Porad

agradable y simple pero no es bueno para una cadena larga ... vote por este buen truco
Maher Abuthraa

Este método parece devolver solo cadenas alfanuméricas en minúsculas.
jaybro

2
Hay palabras vulgares de vez en cuando, pero si mantienes esto funcionando el tiempo suficiente, finalmente escribe Shakespeare. (Solo unas pocas vidas del universo. :)
Slothario

38

Los objetivos principales de mi código son:

  1. La distribución de las cadenas es casi uniforme (no importa las desviaciones menores, siempre que sean pequeñas)
  2. Produce más de unos pocos miles de millones de cadenas para cada conjunto de argumentos. Generar una cadena de 8 caracteres (~ 47 bits de entropía) no tiene sentido si su PRNG solo genera 2 mil millones (31 bits de entropía) de valores diferentes.
  3. Es seguro, ya que espero que la gente use esto para contraseñas u otros tokens de seguridad.

La primera propiedad se logra tomando un módulo de valor de 64 bits del tamaño del alfabeto. Para alfabetos pequeños (como los 62 caracteres de la pregunta) esto lleva a un sesgo insignificante. La segunda y tercera propiedad se logran utilizando en RNGCryptoServiceProviderlugar 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);
}

1
No hay intersección con 64 x Z y Math.Pow (2, Y). Entonces, si bien hacer números más grandes reduce el sesgo, no lo elimina. Actualicé mi respuesta a continuación, mi enfoque era descartar entradas aleatorias y reemplazarlas con otro valor.
Todd

@Todd Sé que no elimina el sesgo, pero elegí la simplicidad de esta solución en lugar de eliminar un sesgo prácticamente irrelevante.
CodesInChaos

Estoy de acuerdo en la mayoría de los casos, probablemente sea prácticamente irrelevante. Pero ahora he actualizado el mío para que sea tan rápido como aleatorio y un poco más seguro que el tuyo. Todo de código abierto para que todos lo compartan. Sí, perdí demasiado tiempo en esto ...
Todd

Si estamos utilizando un proveedor de RNG, ¿tenemos alguna forma de evitar sesgos en teoría? No estoy seguro ... Si Todd se refiere a la forma en que genera un número aleatorio adicional (cuando estamos en la zona de sesgo), entonces puede ser una suposición errónea. RNG tiene una distribución casi lineal de todos los valores generados en promedio. Pero eso no significa que no tendremos correlación local entre los bytes generados. Por lo tanto, el byte adicional solo para la zona de sesgo todavía puede darnos algún sesgo, pero debido a diferentes razones. Lo más probable es que este sesgo sea muy pequeño. PERO en este caso, el aumento del total de bytes generados es una forma más sencilla.
Maxim

1
@Maxim Puede usar el rechazo para eliminar completamente el sesgo (suponiendo que el generador subyacente sea perfectamente aleatorio). A cambio, el código puede correr arbitrariamente largo (con una probabilidad exponencialmente pequeña)
CodesInChaos

32

Lo más simple:

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

Puede obtener un mejor rendimiento si codifica la matriz de caracteres y confía en System.Random:

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

Si alguna vez le preocupa, los alfabetos en inglés pueden cambiar en algún momento y puede perder negocios, entonces puede evitar la codificación rígida, pero debería funcionar ligeramente peor (comparable al Path.GetRandomFileNameenfoque)

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

Los dos últimos enfoques se ven mejor si puede convertirlos en un método de extensión, por System.Randomejemplo.


1
El uso chars.Selectes muy feo ya que depende del tamaño de salida como máximo del tamaño del alfabeto.
CodesInChaos

@CodesInChaos No estoy seguro si te entiendo. ¿Quieres decir en el 'a'.To('z')enfoque?
nawfal

1
1) chars.Select().Take (n) `solo funciona si chars.Count >= n. Seleccionar una secuencia que en realidad no utilizas es poco intuitiva, especialmente con esa restricción de longitud implícita. Prefiero usar Enumerable.Rangeo Enumerable.Repeat. 2) El mensaje de error "el carácter final debe ser menor que el carácter inicial" es el camino equivocado / falta a not.
CodesInChaos

@CodesInChaos pero en mi caso chars.Countes así > n. Además, no entiendo la parte no intuitiva. ¿Eso hace que todos los usos Takeno sean intuitivos? No lo creo Gracias por señalar el error tipográfico.
nawfal

44
Esto aparece en theDailyWTF.com como un artículo de CodeSOD.

22

Solo algunas comparaciones de rendimiento de las diversas respuestas en este hilo:

Métodos y configuración

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

Resultados

Probado en LinqPad. Para un tamaño de cadena de 10, genera:

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

Y las cifras de rendimiento tienden a variar un poco, muy de vez en cuando NonOptimizeden realidad es más rápido, y, a veces ForLoop, y GenerateRandomStringel interruptor que está en la delantera.

  • LinqIsTheNewBlack (10000x) = 96762 tics transcurridos (9.6762 ms)
  • ForLoop (10000x) = 28970 tics transcurridos (2.897 ms)
  • ForLoopNonOptimized (10000x) = 33336 tics transcurridos (3.3336 ms)
  • Repetir (10000x) = 78547 tics transcurridos (7.8547 ms)
  • GenerateRandomString (10000x) = 27416 tics transcurridos (2.7416 ms)
  • SecureFastRandom (10000x) = 13176 tics transcurridos (5 ms) más bajo [máquina diferente]
  • SecureFastRandom-NoCache (10000x) = 39541 tics transcurridos (17 ms) más bajo [máquina diferente]

3
Sería interesante saber cuáles crearon embaucados.
Rebecca

@Junto - averiguar qué resultados por duplicado, algo así como var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, en los que se reemplaza EachRandomizingMethodcon cada método ...
drzaus

19

Una línea de código Membership.GeneratePassword()hace el truco :)

Aquí hay una demostración para lo mismo.



Pensé en eso, pero no pude deshacerme de los caracteres no alfanuméricos, ya que el segundo argumento son los caracteres MÍNIMOS no alfa
ozzy432836

13

El código escrito por Eric J. es bastante descuidado (está bastante claro que es de hace 6 años ... probablemente no escribiría ese código hoy), e incluso hay algunos problemas.

A diferencia de algunas de las alternativas presentadas, esta es criptográficamente sólida.

No es cierto ... Hay un sesgo en la contraseña (como está escrito en un comentario), bcdefghson un poco más probables que los demás ( ano lo es porque GetNonZeroBytesno genera bytes con un valor de cero, por lo que el sesgo porque aestá equilibrado por él), por lo que no es realmente criptográficamente sólido.

Esto debería corregir todos los problemas.

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}

7

También usamos cadenas personalizadas al azar, pero implementamos como ayudante de cadena, por lo que proporciona cierta flexibilidad ...

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

Uso

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

o

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

7

Mi código de una línea simple funciona para mí :)

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

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

Para expandir esto para cualquier cadena de longitud

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

Me gusta el método GUID también - se siente muy ligero
ozzy432836

6

Otra opción podría ser usar Linq y agregar caracteres aleatorios en un generador de cadenas.

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

Pregunta: ¿Por qué debería perder mi tiempo usando en Enumerable.Rangelugar de escribir "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;
    }
}

Respuesta: Las cuerdas mágicas son MALAS. ¿ALGUIEN notó que no había " I" en mi cadena en la parte superior? Mi madre me enseñó a no usar cuerdas mágicas por esta misma razón ...

Nota 1: como muchos otros como @dtb dijeron, no lo use System.Randomsi necesita seguridad criptográfica ...

Nota 2: Esta respuesta no es la más eficiente ni la más breve, pero quería que el espacio separara la respuesta de la pregunta. El propósito de mi respuesta es más advertir contra los hilos mágicos que proporcionar una respuesta innovadora y elegante.


¿Por qué me importa que no haya un " I?"
Christine

1
Alfanumérico (ignorando el caso) es [A-Z0-9]. Si, por accidente, su cadena aleatoria solo cubre [A-HJ-Z0-9]el resultado, no cubre el rango completo permitido, lo que puede ser problemático.
Wai Ha Lee

¿Cómo sería eso problemático? Entonces no contiene I. ¿Es porque hay un personaje menos y eso hace que sea más fácil de descifrar? ¿Cuáles son las estadísticas sobre las contraseñas descifrables que contienen 35 caracteres en el rango de 36. Creo que prefiero arriesgarme ... o simplemente probar el rango de caracteres ... que incluir toda esa basura extra en mi código. Pero ese soy yo. Quiero decir, no ser un agujero en el trasero, solo digo. A veces pienso que los programadores tienen una tendencia a ir por la ruta extra compleja por el simple hecho de ser extracomplejos.
Christine

1
Se profundiza en el caso de uso. Es muy común excluir caracteres como Iy Ode este tipo de cadenas aleatorias para evitar que los humanos los confundan con 1y 0. Si no te importa tener una cadena legible por humanos, está bien, pero si es algo que alguien podría necesitar escribir, entonces es realmente inteligente eliminar esos caracteres.
Chris Pratt

5

Una versión ligeramente más limpia de la solución 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);

Sus preferencias de estilo pueden variar.


Esto es mucho mejor y más eficiente que la respuesta aceptada.
Wedge

5
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }

5

Después de revisar las otras respuestas y considerar los comentarios de CodeInChaos, junto con CodeInChaos aún sesgada (aunque menos), pensé en una solución final definitiva de cortar y pegar se necesitaba una . Entonces, mientras actualizaba mi respuesta, decidí hacer todo lo posible.

Para obtener una versión actualizada de este código, visite el nuevo repositorio de Hg en Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Le recomiendo que copie y pegue el código de: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.fc . el botón Raw para que sea más fácil copiar y asegurarse de tener la última versión, creo que este enlace va a una versión específica del código, no a la última).

Notas actualizadas:

  1. En relación con algunas otras respuestas: si conoce la longitud de la salida, no necesita un StringBuilder, y cuando usa ToCharArray, esto crea y llena la matriz (no necesita crear primero una matriz vacía)
  2. En relación con algunas otras respuestas: debe usar NextBytes, en lugar de obtener una por vez para el rendimiento
  3. Técnicamente, podría anclar la matriz de bytes para un acceso más rápido ... generalmente vale la pena cuando itera más de 6-8 veces sobre una matriz de bytes. (No hecho aquí)
  4. Uso de RNGCryptoServiceProvider para la mejor aleatoriedad
  5. El uso del almacenamiento en caché de un búfer de 1 MB de datos aleatorios : la evaluación comparativa muestra que la velocidad de acceso de bytes individuales en caché es ~ 1000 veces más rápida, lo que lleva 9 ms sobre 1 MB frente a 989 ms para el almacenamiento en caché.
  6. Rechazo optimizado de la zona de sesgo dentro de mi nueva clase.

Solución final a la pregunta:

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

Pero necesitas mi nueva clase (no probada):

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

Para el historial: mi solución anterior para esta respuesta, utilizaba un objeto aleatorio:

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

Actuación:

  1. SecureFastRandom : primera ejecución única = ~ 9-33 ms . Imperceptible. En curso : 5 ms (a veces sube hasta 13 ms) en más de 10,000 iteraciones, con una única iteración promedio = 1.5 microsegundos.. Nota: Requiere generalmente 2, pero ocasionalmente hasta 8 actualizaciones de caché; depende de cuántos bytes individuales superen la zona de sesgo
  2. Aleatorio : primera ejecución única = ~ 0-1 ms . Imperceptible. En curso : 5 ms en más de 10,000 iteraciones. Con una sola iteración promedio = .5 microsegundos. . Sobre la misma velocidad.

También echa un vistazo a:

Estos enlaces son otro enfoque. El almacenamiento en búfer se podría agregar a esta nueva base de código, pero lo más importante fue explorar diferentes enfoques para eliminar el sesgo y comparar las velocidades y las ventajas y desventajas.


Encontré algunas mejoras leves en el rendimiento de su método, que parecía el ayuno del grupo - stackoverflow.com/a/17092645/1037948
drzaus

55
1) ¿Por qué todas esas constantes mágicas? Especificó la longitud de salida tres veces. Simplemente defínalo como una constante o un parámetro. Puedes usar en charSet.Lengthlugar de 62. 2) Una estática Randomsin bloqueo significa que este código no es seguro para subprocesos. 3) reducir 0-255 mod 62 introduce un sesgo detectable. 4) No se puede usar ToStringen una matriz de caracteres, que siempre regresa "System.Char[]". Necesitas usar new String(rName)en su lugar.
CodesInChaos

Gracias @CodesInChaos, nunca pensé en esas cosas en ese entonces. Todavía solo uso la clase Random, pero esto debería ser mejor. No podría pensar en una mejor manera de detectar y corregir las entradas de sesgo.
Todd

Es un poco tonto comenzar con un RNG débil ( System.Random) y luego evitar cuidadosamente cualquier sesgo en su propio código. La expresión "pulir un turd" viene a mi mente.
CodesInChaos

@CodesInChaos Y ahora el aprendiz ha superado a su maestro
Todd

4

Horrible, lo sé, pero no pude evitarlo:


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

Estaba buscando una respuesta más específica, donde quiero controlar el formato de la cadena aleatoria y encontré esta publicación. Por ejemplo: las placas (de automóviles) tienen un formato específico (por país) y quería crear placas aleatorias.
Decidí escribir mi propio método de extensión de Random para esto. (Esto es para reutilizar el mismo objeto aleatorio, ya que podría tener dobles en escenarios de subprocesos múltiples). Creé una esencia ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), pero también copiaré la clase de extensión aquí:

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

Ahora en sabor de una sola línea.

private string RandomName()
{
        return new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    using (var cryptoProvider = new RNGCryptoServiceProvider())
                        cryptoProvider.GetBytes(cryptoResult);

                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());
}

2
Usar una propiedad para algo que cambia en cada acceso es bastante dudoso. Recomiendo usar un método en su lugar.
CodesInChaos

2
RNGCryptoServiceProviderdebe desecharse después de su uso.
Tsahi Asher

Solucioné el problema de IDisposable, pero esto todavía es muy dudoso, creando un nuevo RNGCryptoServiceProvider para cada letra.
piojo

@CodesInChaos hecho, ahora un método.
Matas Vaitkevicius

3

Intente combinar dos partes: única (secuencia, contador o fecha) y aleatoria

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

Pruebas:

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

1) Puede usar literales de caracteres en lugar de los valores ASCII asociados con esos caracteres. 2) Tiene un error off-by-one en su código de coincidencia de intervalo. Necesita usar <=y en >=lugar de <y >. 3) Agregaría los paréntesis innecesarios alrededor de las &&expresiones para dejar en claro que tienen prioridad, pero, por supuesto, eso es solo una elección estilística.
CodesInChaos

+ 1 Bueno para eliminar sesgos y agregar pruebas. Sin embargo, no estoy seguro de por qué antepone su cadena aleatoria con una cadena derivada de marca de tiempo. Además, todavía se necesita deshacerse de su RNGCryptoServiceProvider
Monty

2

Una solución sin usar Random:

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

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

2
NewGuid usa aleatoriamente internamente. Entonces esto todavía se usa al azar, solo lo está ocultando.
Wedge

2

Aquí hay una variante de la solución de Eric J, es decir, criptográficamente sólida, para WinRT (aplicación de la Tienda Windows):

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 el rendimiento es importante (especialmente cuando la longitud es alta):

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

1
Esto no es criptográficamente sólido. Hay un pequeño sesgo debido a que la operación del módulo no extiende el ancho completo de ulong por igual en 62 caracteres.
Mentira Ryan

1

Sé que esta no es la mejor manera. Pero puedes probar esto.

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

2
¿Cómo es esa línea? Console.WriteLine ($ "Cadena aleatoria: {Path.GetRandomFileName (). Reemplazar (". "," ")}"); Es una línea.
PmanAce

1

No sé cuán criptográficamente suena esto, pero es mucho más legible y conciso que las soluciones más complejas con diferencia (imo), y debería ser más "aleatorio" que las System.Randomsoluciones basadas.

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

No puedo decidir si creo que esta versión o la siguiente es "más bonita", pero dan exactamente los mismos resultados:

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

De acuerdo, no está optimizado para la velocidad, por lo que si es fundamental generar millones de cadenas aleatorias cada segundo, ¡pruebe con otra!

NOTA: Esta solución no permite la repetición de símbolos en el alfabeto, y el alfabeto DEBE ser de igual o mayor tamaño que la cadena de salida, lo que hace que este enfoque sea menos deseable en algunas circunstancias, todo depende de su caso de uso.


0

Si sus valores no son completamente al azar, pero de hecho pueden depender de algo, puede calcular un hash md5 o sha1 de ese 'algo' y luego truncarlo a la longitud que desee.

También puede generar y truncar un guid.


0
public static class StringHelper
{
    private static readonly Random random = new Random();

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

    private static int randomSymbolsIndex = 0;

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

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

2
1) los métodos estáticos deben ser seguros para los hilos 2) ¿Cuál es el punto de incrementar el índice en lugar de usar el resultado random.Nextdirectamente? Complica el código y no logra nada útil.
CodesInChaos

0

Aquí hay un mecanismo para generar una cadena alfanumérica aleatoria (la uso para generar contraseñas y datos de prueba) sin definir el alfabeto y los números,

CleanupBase64 eliminará las partes necesarias en la cadena y seguirá agregando letras alfanuméricas aleatorias de forma recursiva.

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

Has declarado GenerateRandomStringy has hecho una llamada GetRandomStringdesde dentro SanitiseBase64String. También se ha declarado SanitiseBase64String y llamada CleanUpBase64Stringen GenerateRandomString.
Wai Ha Lee

0

Existe uno de los increíbles paquetes nuget que lo hacen tan simple.

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

Uno de los buenos ejemplos está aquí .


0

no estoy 100% seguro, ya que no probé CADA opción aquí, pero de las que probé, esta es la más rápida. lo cronometró con cronómetro y mostró 9-10 tics, así que si la velocidad es más importante que la seguridad, intente esto:

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

¿Por qué responder? Esta pregunta ha sido respondida muchas veces y aún así publicas tu respuesta a medias.
Laurent
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.