Como posso gerar seqüências alfanuméricas aleatórias?

968

Como posso gerar uma seqüência alfanumérica aleatória de 8 caracteres em C #?

KingNestor
fonte
2
Quais restrições você tem no conjunto de caracteres? Apenas caracteres do idioma inglês e 0-9? Caso misto?
Eric J.
9
Observe que você NÃO deve usar nenhum método baseado na Randomclasse para gerar senhas. A semeadura Randomtem entropia muito baixa, portanto não é realmente seguro. Use um PRNG criptográfico para senhas.
CodesInChaos
2
Seria bom incluir a localização de idiomas nesta pergunta. Especialmente se o seu gui precisar atender chinês ou búlgaro!
Peter Jamsmenson
15
Algo com tantos votos positivos e tantas respostas de qualidade não merece ser marcado como fechado. Eu voto para que seja reaberto.
John Coleman

Respostas:

1686

Ouvi dizer que o LINQ é o novo preto, então aqui está minha tentativa de usar o 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());
}

(Observação: o uso da Randomclasse torna isso inadequado para qualquer coisa relacionada à segurança , como a criação de senhas ou tokens. Use a RNGCryptoServiceProviderclasse se precisar de um gerador de números aleatórios forte.)

dtb
fonte
24
@ Alex: Eu executei alguns testes rápidos e parece ter uma escala linear ao gerar strings mais longas (desde que haja realmente memória suficiente disponível). Dito isto, a resposta de Dan Rigby foi quase duas vezes mais rápida que essa em todos os testes.
LukeH
6
Bem. Se o seu critério é que ele use linq e tenha uma péssima narrativa de código, então é definitivamente o joelho da abelha. Tanto a narrativa do código quanto o caminho real da execução são ineficientes e indiretos. Não me interpretem mal, eu sou um grande hipster de código (eu amo python), mas isso é praticamente uma máquina rube goldberg.
precisa saber é o seguinte
5
Embora isso tecnicamente responda à pergunta, sua saída é muito enganadora. Gerando 8 sons caracteres aleatórios como não pode haver muito muitos resultados, que esta na melhor das hipóteses produz 2 bilhões de resultados diferentes. E na prática ainda menos. Você também deve adicionar um aviso BIG FAT para não usá-lo para nada relacionado à segurança.
CodesInChaos
41
@xaisoft: as letras minúsculas são deixadas como exercício para o leitor.
dtb
15
A linha a seguir é mais eficiente em termos de memória (e, portanto, em tempo) do que a dadareturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Tyson Williams
376
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

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

var finalString = new String(stringChars);

Não é tão elegante quanto a solução Linq.

(Observação: o uso da Randomclasse torna isso inadequado para qualquer coisa relacionada à segurança , como a criação de senhas ou tokens. Use a RNGCryptoServiceProviderclasse se precisar de um gerador de números aleatórios forte.)

Dan Rigby
fonte
4
@ Alex: Esta não é a resposta mais rápida absoluta, mas é a resposta "real" mais rápida (ou seja, daquelas que permitem o controle sobre os caracteres usados ​​e o comprimento da string).
LukeH
2
@Alex: A GetRandomFileNamesolução de Adam Porad é mais rápida, mas não permite nenhum controle dos caracteres utilizados e o tamanho máximo possível é de 11 caracteres. A Guidsolução de Douglas é extremamente rápida, mas os caracteres são restritos a A-F0-9 e o comprimento máximo possível é de 32 caracteres.
LukeH
1
@ Adam: Sim, você pode concordar com o resultado de várias chamadas, GetRandomFileNamemas (a) perderia sua vantagem de desempenho e (b) seu código se tornaria mais complicado.
LukeH
2
@xaisoft crie sua instância do objeto Random () fora do seu loop. Se você criar várias instâncias de Random () em um curto intervalo, a chamada para .Next () retornará o mesmo valor que Random () usará uma semente baseada em tempo.
Dan Rigby
2
@xaisoft Não use esta resposta para nada crítico de segurança, como senhas. System.Randomnão é adequado para segurança.
CodesInChaos
333

ATUALIZADO com base nos comentários. A implementação original gerou ah ~ 1,95% do tempo e os caracteres restantes ~ 1,56% do tempo. A atualização gera todos os caracteres ~ 1,61% do tempo.

O .NET Core 3 (e futuras plataformas que oferecem suporte ao .NET Standard 2.1 ou superior) fornece um método criptograficamente sólido RandomNumberGenerator.GetInt32 () para gerar um número inteiro aleatório dentro de um intervalo desejado.

Ao contrário de algumas das alternativas apresentadas, essa é criptograficamente 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();
        }
    }
}

Baseado em uma discussão de alternativas aqui e atualizado / modificado com base nos comentários abaixo.

Aqui está um pequeno equipamento de teste que demonstra a distribuição de caracteres na saída antiga e atualizada. Para uma discussão profunda da análise da aleatoriedade , consulte random.org.

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

namespace CryptoRNGDemo
{
    class Program
    {

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

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

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

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

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

            int totalChars = counts.Values.Sum();
            foreach (var ch in UniqueKey.KeyGenerator.chars)
            {
                Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
            }
        }
    }
}
Eric J.
fonte
11
Isso parece a abordagem correta para mim - senhas aleatórias, sais, entropia e assim por diante não devem ser geradas usando Random (), que é otimizado para velocidade e gera seqüências reproduzíveis de números; Por outro lado, RNGCryptoServiceProvider.GetNonZeroBytes () produz seqüências curtas de números que NÃO são reproduzíveis.
mindplay.dk
25
As letras são ligeiramente tendenciosas (255% 62! = 0). Apesar dessa pequena falha, é de longe a melhor solução aqui.
CodesInChaos
13
Observe que isso não é válido se você deseja aleatoriedade imparcial e com força de criptografia. (E se você não quer isso, então por que o uso RNGCSPem primeiro lugar?) Usando mod para indexar nos charsmeios de matriz que você vai ser tendenciosa saída a menos que chars.Lengthpassa a ser um divisor de 256.
LukeH
15
Uma possibilidade de reduzir muito o viés é solicitar 4*maxSizebytes aleatórios e usar (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Eu também usaria em GetBytesvez de GetNonZeroBytes. E, finalmente, você pode remover a primeira chamada para GetNonZeroBytes. Você não está usando o resultado.
CodesInChaos
14
Curiosidade: AZ az 0-9 tem 62 caracteres. As pessoas estão apontando o viés da letra porque 256% 62! = 0. Os IDs de vídeo do YouTube são AZ az 0-9, além de "-" e "_", que produz 64 caracteres possíveis, divididos em 256 uniformemente. Coincidência? Eu acho que não! :)
qJake
200

Solução 1 - maior 'faixa' com comprimento mais flexível

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 solução tem mais alcance do que usar um GUID porque um GUID possui alguns bits fixos que são sempre os mesmos e, portanto, não aleatórios, por exemplo, o caractere 13 em hexadecimal é sempre "4" - pelo menos em um GUID da versão 6.

Essa solução também permite gerar uma sequência de qualquer tamanho.

Solução 2 - Uma linha de código - válida para até 22 caracteres

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

Você não pode gerar cadeias de caracteres desde que a Solução 1 e a cadeia de caracteres não tenham o mesmo intervalo devido a bits fixos nos GUIDs, mas em muitos casos isso fará o trabalho.

Solução 3 - um pouco menos de código

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

Principalmente mantendo isso aqui para fins históricos. Ele usa um pouco menos de código, que, apesar de custar menos, tem um alcance - porque usa hex em vez de base64, são necessários mais caracteres para representar o mesmo intervalo em comparação com as outras soluções.

O que significa mais chances de colisão - testá-lo com 100.000 iterações de 8 cadeias de caracteres gerou uma duplicata.

Douglas
fonte
22
Você realmente gerou uma duplicata? Surpreendente em 5.316.911.983.139.663.491.615.228.241.121.400.000 combinações possíveis de GUIDs.
Alex
73
@Alex: ele está encurtando o GUID para 8 caracteres, então a probabilidade de colisões é muito maior do que a dos GUIDs.
dtb 28/08/09
23
Ninguém pode apreciar isso além de nerds :) Sim, você está absolutamente certo, o limite de 8 caracteres faz a diferença.
2828 Alex
31
Guid.NewGuid (). ToString ("n") manterá os traços afastados, não é necessária a chamada Replace (). Mas deve ser mencionado, os GUIDs são apenas 0-9 e AF. O número de combinações é "bom o suficiente", mas nem de perto o que uma verdadeira seqüência aleatória alfanumérica permite. As chances de colisão são de 1: 4.294.967.296 - o mesmo que um número inteiro aleatório de 32 bits.
richardtallent 28/08/09
32
1) Os GUIDs são projetados para serem exclusivos, não aleatórios. Embora as versões atuais do Windows gerem GUIDs V4 realmente aleatórios, isso não é garantido. Por exemplo, versões mais antigas do Windows usavam GUIDs V1, onde o seu poderia falhar. 2) O simples uso de caracteres hexadecimais reduz significativamente a qualidade da sequência aleatória. De 47 a 32 bits. 3) As pessoas estão subestimando a probabilidade de colisão, uma vez que a dão para pares individuais. Se você gerar 100k valores de 32 bits, provavelmente terá uma colisão entre eles. Consulte Problema de aniversário.
CodesInChaos
72

Aqui está um exemplo que eu roubei do exemplo de Sam Allen no Dot Net Perls

Se você precisar apenas de 8 caracteres, use Path.GetRandomFileName () no espaço para nome System.IO. Sam diz que o uso do método "Path.GetRandomFileName aqui às vezes é superior, porque usa RNGCryptoServiceProvider para obter melhor aleatoriedade. No entanto, é limitado a 11 caracteres aleatórios".

GetRandomFileName sempre retorna uma cadeia de 12 caracteres com um ponto no nono caractere. Portanto, você precisará retirar o período (já que isso não é aleatório) e, em seguida, pegar 8 caracteres da sequência. Na verdade, você pode apenas pegar os 8 primeiros caracteres e não se preocupar com o período.

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

PS: obrigado Sam

Adam Porad
fonte
27
Isso funciona bem. Eu o executei através de 100.000 iterações e nunca tive um nome duplicado. No entanto, eu queria encontrar várias palavras vulgares (em Inglês). Nem pensaria nisso, exceto que um dos primeiros da lista tinha F *** nele. Apenas um alerta se você usar isso para algo que o usuário verá.
Techturtle 9/10/12
3
@techturtle Obrigado pelo aviso. Suponho que exista o risco de palavras vulgares com qualquer geração aleatória de strings que use todas as letras do alfabeto.
Adam Porad 10/10/12
agradável e simples, mas não é bom para longa seqüência ... voto para este bom truque
Maher Abuthraa
Esse método parece retornar apenas seqüências alfanuméricas em minúsculas.
Jaybro #
2
De vez em quando, há palavras vulgares, mas se você continuar assim por algum tempo, escreverá Shakespeare. (Apenas algumas vidas do universo :).
Slothario
38

Os principais objetivos do meu código são:

  1. A distribuição das strings é quase uniforme (não se preocupe com pequenos desvios, desde que sejam pequenos)
  2. Ele gera mais de alguns bilhões de strings para cada conjunto de argumentos. Gerar uma seqüência de 8 caracteres (~ 47 bits de entropia) não faz sentido se o seu PRNG gerar apenas 2 bilhões (31 bits de entropia) de valores diferentes.
  3. É seguro, pois espero que as pessoas usem isso para senhas ou outros tokens de segurança.

A primeira propriedade é obtida assumindo um módulo de valor de 64 bits no tamanho do alfabeto. Para alfabetos pequenos (como os 62 caracteres da pergunta), isso leva a um viés desprezível. A segunda e a terceira propriedade são obtidas usando em RNGCryptoServiceProvidervez de System.Random.

using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}
CodesInChaos
fonte
1
Não há interseção com 64 x Z e Math.Pow (2, Y). Portanto, embora fazer números maiores reduza o viés, ele não o elimina. Atualizei minha resposta abaixo, minha abordagem era descartar entradas aleatórias e substituir por outro valor.
Todd
@ Eu sei que isso não elimina o viés, mas escolhi a simplicidade desta solução em vez de eliminar um viés praticamente irrelevante.
CodesInChaos
Concordo que, na maioria dos casos, é provavelmente praticamente irrelevante. Mas agora atualizei o meu para ser tão rápido quanto aleatório e um pouco mais seguro que o seu. Todos de código aberto para todos compartilharem. Sim, eu perdi tempo demais sobre isso ...
Todd
Se estamos usando o provedor RNG, temos alguma maneira de evitar viés na teoria? Não tenho certeza ... Se Todd está falando sério quando ele gera um número aleatório adicional (quando estamos na zona de viés), pode ser uma suposição errada. RNG tem distribuição quase linear de todos os valores gerados em média. Mas isso não significa que não teremos correlação local entre os bytes gerados. Assim, o byte adicional apenas para a zona de polarização ainda pode nos dar alguma polarização, mas devido a uma razão diferente. Muito provavelmente esse viés será muito pequeno. MAS, neste caso, o aumento do total de bytes gerados é uma maneira mais direta.
Maxim
1
@ Maxim Você pode usar a rejeição para eliminar completamente o viés (assumindo que o gerador subjacente seja perfeitamente aleatório). Em troca, o código pode ser arbitrariamente longo (com probabilidade exponencialmente pequena).
CodesInChaos 31/07
32

O mais simples:

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

Você pode obter um melhor desempenho se codificar a matriz char e confiar em System.Random:

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

Se alguma vez você se preocupar, os alfabetos ingleses podem mudar em algum momento e você pode perder negócios, evitando o código rígido, mas deve ter um desempenho um pouco pior (comparável à Path.GetRandomFileNameabordagem)

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

As duas últimas abordagens parecem melhores se você puder torná-las um método de extensão por System.Randomexemplo.

nawfal
fonte
1
Usar chars.Selecté muito feio, pois depende do tamanho da saída ser no máximo o tamanho do alfabeto.
CodesInChaos
@CodesInChaos Não tenho certeza se entendo você. Você quer dizer na 'a'.To('z')abordagem?
Nawfal #
1
1). chars.Select()Take (n) `só funciona se chars.Count >= n. Selecionar uma sequência que você realmente não usa é um pouco pouco intuitivo, especialmente com essa restrição implícita de comprimento. Eu prefiro usar Enumerable.Rangeou Enumerable.Repeat. 2) A mensagem de erro "o caractere final deve ser menor que o caractere inicial" é o caminho errado / ausente a not.
precisa saber é o seguinte
@CodesInChaos, mas no meu caso chars.Counté o caminho > n. Também não entendo a parte não intuitiva. Isso faz com que todos os usos Takenão sejam intuitivos, não é? Eu não acredito nisso. Obrigado por apontar o erro de digitação.
Nawfal
4
Isso é apresentado no theDailyWTF.com como um artigo do CodeSOD.
22

Apenas algumas comparações de desempenho das várias respostas neste tópico:

Métodos e configuração

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

Testado no LinqPad. Para o tamanho da sequência de 10, gera:

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

E os números de desempenho tendem a variar um pouco, muito ocasionalmente NonOptimizedé realmente mais rápido, e às vezes ForLoope GenerateRandomStringmudar quem está na liderança.

  • LinqIsTheNewBlack (10000x) = 96762 ticks decorridos (9.6762 ms)
  • ForLoop (10000x) = 28970 ticks decorridos (2.897 ms)
  • ForLoopNonOptimized (10000x) = 33336 ticks decorridos (3.3336 ms)
  • Repita (10000x) = 78547 tiques decorridos (7,8547 ms)
  • GenerateRandomString (10000x) = 27416 ticks decorridos (2,7416 ms)
  • SecureFastRandom (10000x) = 13176 ticks decorridos (5 ms) mais baixos [Máquina diferente]
  • SecureFastRandom-NoCache (10000x) = 39541 ticks decorridos (17ms) mais baixos [Máquina diferente]
drzaus
fonte
3
Seria interessante saber quais criaram bobagens.
Rebecca
@Junto - para descobrir o que resulta em duplicatas, algo como var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, onde você substitui EachRandomizingMethodcom ... cada método
drzaus
19

Uma linha de código Membership.GeneratePassword()faz o truque :)

Aqui está uma demonstração para o mesmo.

Pooran
fonte
1
Parece que a Microsoft moveu o link .. outro exemplo de código está em msdn.microsoft.com/en-us/library/ms152017 ou aspnet.4guysfromrolla.com/demos/GeneratePassword.aspx ou developer.xamarin.com/api/member/…
Pooran
Pensei nisso, mas não conseguiu livrar-se dos caracteres não alfanuméricos como o segundo argumento é o caracteres alfabéticos não MÍNIMOS
ozzy432836
13

O código escrito por Eric J. é bastante desleixado (é claro que é de 6 anos atrás ... ele provavelmente não escreveria esse código hoje), e existem até alguns problemas.

Ao contrário de algumas das alternativas apresentadas, essa é criptograficamente sólida.

Falso ... Há um viés na senha (conforme escrito em um comentário), bcdefghé um pouco mais provável que os outros ( anão é porque, pelo GetNonZeroBytesfato de não estar gerando bytes com um valor zero, então o viés pois o aé equilibrado por ele), por isso não é realmente criptograficamente sólido.

Isso deve corrigir todos os 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);
    }
}
xanatos
fonte
7

Também usamos aleatoriamente uma string personalizada, mas implementamos como auxiliar de uma string, por isso fornece alguma flexibilidade ...

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

ou

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

Meu código de uma linha simples funciona para mim :)

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

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

Para expandir isso para qualquer string de comprimento

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }
Raj kumar
fonte
Também gosto do método guid - parece realmente leve
ozzy432836:
6

Outra opção poderia ser usar o Linq e agregar caracteres aleatórios em um construtor de strings.

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());
AAD
fonte
6

Pergunta: Por que devo perder meu tempo usando, em Enumerable.Rangevez de digitar "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;
    }
}

Resposta: Cordas mágicas são ruins. ALGUÉM notou que não havia "I " na minha corda no topo? Minha mãe me ensinou a não usar cordas mágicas por esse motivo ...

Nota 1: Como muitos outros como @dtb disseram, não use System.Randomse você precisar de segurança criptográfica ...

Nota 2: Esta resposta não é a mais eficiente ou a mais curta, mas eu queria que o espaço separasse a resposta da pergunta. O objetivo da minha resposta é mais alertar contra as cordas mágicas do que fornecer uma resposta inovadora e sofisticada.

Wai Ha Lee
fonte
Por que me importo que não haja " I?"
Christine
1
Alfanumérico (caso de ignorância) é [A-Z0-9]. Se, por acidente, sua sequência aleatória apenas cobrir [A-HJ-Z0-9]o resultado, não cobrirá todo o intervalo permitido, o que pode ser problemático.
Wai Ha Lee
Como isso seria problemático? Portanto, não contém I. É porque há um personagem a menos e isso facilita a quebra? Quais são as estatísticas de senhas quebráveis ​​que contêm 35 caracteres no intervalo que 36. Acho que prefiro arriscar ... ou apenas provar o intervalo de caracteres ... do que incluir todo esse lixo extra no meu código. Mas sou eu. Quero dizer, para não ser um buraco de bunda, só estou dizendo. Às vezes, acho que os programadores tendem a seguir a rota extra-complexa, a fim de serem extra-complexos.
Christine
1
Depende do caso de uso. É muito comum excluir caracteres como Ie Odesses tipos de seqüências aleatórias para evitar que os humanos os confundam com 1e 0. Se você não se importa em ter uma sequência legível por humanos, tudo bem, mas se for algo que alguém precise digitar, é realmente inteligente remover esses caracteres.
Chris Pratt
5

Uma versão ligeiramente mais limpa da solução da 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);

Suas preferências de estilo podem variar.

Rob Deary
fonte
Isso é muito melhor e mais eficiente do que a resposta aceita.
Wedge
5
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
Tejas
fonte
5

Depois de revisar as outras respostas e considerar os comentários do CodeInChaos, junto com a resposta ainda tendenciosa (embora menor) do CodeInChaos, pensei que era necessária uma solução final de recortar e colar . Então, enquanto atualizava minha resposta, decidi dar tudo de si.

Para uma versão atualizada desse código, visite o novo repositório Hg no Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Eu recomendo que você copie e cole o código em: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer (certifique-se que você clique o botão Raw para facilitar a cópia e garantir que você tenha a versão mais recente, acho que esse link vai para uma versão específica do código, não a mais recente).

Notas atualizadas:

  1. Relacionado a algumas outras respostas - Se você souber o tamanho da saída, não precisará de um StringBuilder e, ao usar o ToCharArray, isso cria e preenche a matriz (você não precisa criar uma matriz vazia primeiro)
  2. Relacionando-se com outras respostas - Você deve usar o NextBytes, em vez de obter um de cada vez para obter desempenho
  3. Tecnicamente, você pode fixar a matriz de bytes para obter acesso mais rápido. Geralmente, vale a pena iterar mais de 6 a 8 vezes em uma matriz de bytes. (Não feito aqui)
  4. Uso do RNGCryptoServiceProvider para melhor aleatoriedade
  5. Uso do armazenamento em cache de um buffer de 1 MB de dados aleatórios - o benchmarking mostra que a velocidade de acesso de bytes únicos em cache é ~ 1000x mais rápida - levando 9ms acima de 1MB contra 989ms para o cache.
  6. Rejeição otimizada da zona de viés dentro da minha nova classe.

Solução final para a pergunta:

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

Mas você precisa da minha nova classe (não testada):

/// <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 a história - minha solução mais antiga para esta resposta, usei o objeto Random:

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

Atuação:

  1. SecureFastRandom - Primeira execução única = ~ 9-33ms . Imperceptível. Em andamento : 5ms (às vezes chega a 13ms) acima de 10.000 iterações, com uma única iteração média = 1,5 microssegundos.. Nota: Requer geralmente 2, mas ocasionalmente até 8 atualizações de cache - depende de quantos bytes únicos excedem a zona de polarização
  2. Aleatório - Primeira execução única = ~ 0-1ms . Imperceptível. Em andamento : 5ms a mais de 10.000 iterações. Com uma única iteração média = 0,5 microssegundos. . Sobre a mesma velocidade.

Verifique também:

Esses links são outra abordagem. O buffer poderia ser adicionado a essa nova base de código, mas o mais importante era explorar diferentes abordagens para remover desvios e comparar as velocidades e prós / contras.

Merari Schroeder
fonte
Eu encontrei algumas pequenas melhorias de desempenho para o seu método, que parecia o jejum do grupo - stackoverflow.com/a/17092645/1037948
drzaus
5
1) Por que todas essas constantes mágicas? Você especificou o comprimento da saída três vezes. Apenas defina-o como uma constante ou um parâmetro. Você pode usar em charSet.Lengthvez de 62. 2) Uma estática Randomsem bloqueio significa que este código não é seguro para threads. 3) reduzir 0-255 mod 62 introduz um viés detectável. 4) Você não pode usar ToStringem uma matriz de caracteres, que sempre retorna "System.Char[]". Você precisa usar em seu new String(rName)lugar.
CodesInChaos
Obrigado @CodesInChaos, eu nunca pensei nessas coisas naquela época. Ainda usando apenas a classe Random, mas isso deve ser melhor. Eu não conseguia pensar em nenhuma maneira melhor de detectar e corrigir entradas tendenciosas.
Todd
É um pouco tolo começar com um RNG fraco ( System.Random) e evitar cuidadosamente qualquer viés no seu próprio código. A expressão "polir um bosta" vem à mente.
CodesInChaos
@CodesInChaos E agora o aprendiz superou seu mestre
Todd
4

Horrível, eu sei, mas não consegui me conter:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}

James
fonte
4

Eu estava procurando por uma resposta mais específica, onde desejasse controlar o formato da sequência aleatória e me deparei com este post. Por exemplo: matrículas (de carros) têm um formato específico (por país) e eu queria criar matrículas aleatórias.
Eu decidi escrever meu próprio método de extensão do Random para isso. (isso é para reutilizar o mesmo objeto aleatório, pois você pode dobrar em cenários com vários threads). Criei uma lista ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), mas também copio a classe de extensão aqui:

void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}
Sam Vanhoutte
fonte
4

Agora no sabor de uma linha.

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

                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());
}
Matas Vaitkevicius
fonte
2
Usar uma propriedade para algo que muda a cada acesso é bastante duvidoso. Eu recomendo usar um método.
CodesInChaos 11/01
2
RNGCryptoServiceProviderdeve ser descartado após o uso.
Tsahi Asher
Corrigi o problema de IDisposable, mas isso ainda é altamente duvidoso, criando um novo RNGCryptoServiceProvider para cada letra.
Piojo 7/11
@CodesInChaos done, agora um método.
Matas Vaitkevicius 17/04
3

Tente combinar duas partes: única (sequência, contador ou data) e aleatória

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

Testes:

[Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }
RouR
fonte
1) Você pode usar literais de caracteres em vez dos valores ASCII associados a esses caracteres. 2) Você tem um erro de um por um no seu código de correspondência de intervalo. Você precisa usar <=e em >=vez de <e >. 3) Eu adicionaria os parênteses desnecessários em torno das &&expressões para deixar claro que elas têm precedência, mas é claro que isso é apenas uma escolha estilística.
CodesInChaos
+ 1 Bom para remover o viés e adicionar testes. Não sei por que você precede sua sequência aleatória com uma sequência derivada de carimbo de data / hora. Além disso, você ainda precisa descartar seu RNGCryptoServiceProvider
Monty
2

Uma solução sem usar Random:

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

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
wb
fonte
2
O NewGuid usa aleatoriamente internamente. Então, isso ainda está usando aleatoriamente, está apenas escondendo.
Wedge
2

Aqui está uma variante da solução de Eric J, ou seja, criptograficamente sólida, para o 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();
}

Se o desempenho for importante (especialmente quando o comprimento for alto):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}
huyc
fonte
1
Isso não é criptograficamente correto. Há um pequeno viés devido à operação do módulo que não espalha a largura inteira do ulong igualmente em 62 caracteres.
Lie Ryan
1

Eu sei que este não é o melhor caminho. Mas você pode tentar isso.

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
Sagar
fonte
2
Como é essa linha? Console.WriteLine ($ "String aleatória: {Path.GetRandomFileName (). Replace (". "," ")}"); é uma linha.
PmanAce
1

Eu não sei o quão criptograficamente isso é, mas é mais legível e conciso do que as soluções mais complexas de longe (imo), e deve ser mais "aleatório" do que as System.Randomsoluções baseadas.

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

Não consigo decidir se acho que esta versão ou a próxima é "mais bonita", mas elas fornecem exatamente os mesmos resultados:

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

Concedido, não é otimizado para velocidade, portanto, se é essencial gerar milhões de seqüências aleatórias a cada segundo, tente outra!

NOTA: Esta solução não permite repetições de símbolos no alfabeto, e o alfabeto DEVE ter tamanho igual ou superior ao da string de saída, tornando essa abordagem menos desejável em algumas circunstâncias, tudo depende do seu caso de uso.

sara
fonte
0

Se seus valores não são completamente aleatórios, mas na verdade podem depender de algo - você pode calcular um hash md5 ou sha1 desse 'algo' e depois truncá-lo no tamanho que desejar.

Além disso, você pode gerar e truncar um guia.

Alexey B.
fonte
0
public static class StringHelper
{
    private static readonly Random random = new Random();

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

    private static int randomSymbolsIndex = 0;

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

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
KregHEk
fonte
2
1) métodos estáticos devem ser seguros para discussão. 2) Qual o sentido de incrementar o índice em vez de usar o resultado random.Nextdiretamente? Complica o código e não alcança nada útil.
CodesInChaos
0

Aqui está um mecanismo para gerar uma sequência alfanumérica aleatória (eu uso isso para gerar senhas e dados de teste) sem definir o alfabeto e os números,

CleanupBase64 removerá as partes necessárias na string e continuará adicionando letras alfanuméricas aleatórias recursivamente.

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

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }
Dhanuka777
fonte
Você declarou GenerateRandomStringe telefonou GetRandomStringde dentro SanitiseBase64String. Além disso, você ter declarado SanitiseBase64String e chamada CleanUpBase64Stringem GenerateRandomString.
Wai Ha Lee
0

Há um dos pacotes de nuget impressionantes que tornam isso tão simples.

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

Um dos bons exemplos está aqui .

nzrytmn
fonte
0

não tenho 100% de certeza, já que não testei TODAS as opções aqui, mas das que testei, essa é a mais rápida. cronometrou-o com cronômetro e mostrou 9-10 ticks; portanto, se a velocidade é mais importante que a segurança, tente o seguinte:

 private static Random random = new Random(); 
 public static string Random(int length)
     {   
          var stringChars = new char[length];

          for (int i = 0; i < length; i++)
              {
                  stringChars[i] = (char)random.Next(0x30, 0x7a);                  
                  return new string(stringChars);
              }
     }
PetNoire
fonte
Por que responder? Esta pergunta foi respondida várias vezes e, no entanto, você ainda posta sua resposta pela metade do trabalho ..
Laurent