Como posso gerar uma seqüência alfanumérica aleatória de 8 caracteres em C #?
Random
classe para gerar senhas. A semeadura Random
tem entropia muito baixa, portanto não é realmente seguro. Use um PRNG criptográfico para senhas.
Como posso gerar uma seqüência alfanumérica aleatória de 8 caracteres em C #?
Random
classe para gerar senhas. A semeadura Random
tem entropia muito baixa, portanto não é realmente seguro. Use um PRNG criptográfico para senhas.
Respostas:
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 Random
classe torna isso inadequado para qualquer coisa relacionada à segurança , como a criação de senhas ou tokens. Use a RNGCryptoServiceProvider
classe se precisar de um gerador de números aleatórios forte.)
return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
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 Random
classe torna isso inadequado para qualquer coisa relacionada à segurança , como a criação de senhas ou tokens. Use a RNGCryptoServiceProvider
classe se precisar de um gerador de números aleatórios forte.)
GetRandomFileName
soluçã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 Guid
soluçã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.
GetRandomFileName
mas (a) perderia sua vantagem de desempenho e (b) seu código se tornaria mais complicado.
System.Random
não é adequado para segurança.
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")}%");
}
}
}
}
RNGCSP
em primeiro lugar?) Usando mod para indexar nos chars
meios de matriz que você vai ser tendenciosa saída a menos que chars.Length
passa a ser um divisor de 256.
4*maxSize
bytes aleatórios e usar (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length
. Eu também usaria em GetBytes
vez de GetNonZeroBytes
. E, finalmente, você pode remover a primeira chamada para GetNonZeroBytes
. Você não está usando o resultado.
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.
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
Os principais objetivos do meu código são:
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 RNGCryptoServiceProvider
vez 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);
}
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.GetRandomFileName
abordagem)
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.Random
exemplo.
chars.Select
é muito feio, pois depende do tamanho da saída ser no máximo o tamanho do alfabeto.
'a'.To('z')
abordagem?
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.Range
ou Enumerable.Repeat
. 2) A mensagem de erro "o caractere final deve ser menor que o caractere inicial" é o caminho errado / ausente a not
.
chars.Count
é o caminho > n
. Também não entendo a parte não intuitiva. Isso faz com que todos os usos Take
não sejam intuitivos, não é? Eu não acredito nisso. Obrigado por apontar o erro de digitação.
Apenas algumas comparações de desempenho das várias respostas neste tópico:
// 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);
}
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 ForLoop
e GenerateRandomString
mudar 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]
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());
, onde você substitui EachRandomizingMethod
com ... cada método
Uma linha de código Membership.GeneratePassword()
faz o truque :)
Aqui está uma demonstração para o mesmo.
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 ( a
não é porque, pelo GetNonZeroBytes
fato 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);
}
}
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);
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);
}
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());
Pergunta: Por que devo perder meu tempo usando, em Enumerable.Range
vez 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.Random
se 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.
I
?"
[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.
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.
I
e O
desses tipos de seqüências aleatórias para evitar que os humanos os confundam com 1
e 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.
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.
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:
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:
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.
charSet.Length
vez de 62
. 2) Uma estática Random
sem 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 ToString
em uma matriz de caracteres, que sempre retorna "System.Char[]"
. Você precisa usar em seu new String(rName)
lugar.
System.Random
) e evitar cuidadosamente qualquer viés no seu próprio código. A expressão "polir um bosta" vem à mente.
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);
}
}
}
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)];
}
}
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());
}
RNGCryptoServiceProvider
deve ser descartado após o uso.
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())
}
<=
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.
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());
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();
}
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);
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.Random
soluçõ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.
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;
}
}
random.Next
diretamente? Complica o código e não alcança nada útil.
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);
}
GenerateRandomString
e telefonou GetRandomString
de dentro SanitiseBase64String
. Além disso, você ter declarado SanitiseBase64String
e chamada CleanUpBase64String
em GenerateRandomString
.
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 .
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);
}
}