How can I generate a random 8 character alphanumeric string in C#?
I heard LINQ is the new black, so here's my attempt using 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());
}
(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)
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);
Not as elegant as the Linq solution.
(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)
UPDATED for .NET 6. RNGCryptoServiceProvider is marked as obsolete. Instead, call RandomNumberGenerator.Create(). The code in the answer has been updated accordingly.
UPDATED based on comments. The original implementation generated a-h ~1.95% of the time and the remaining characters ~1.56% of the time. The update generates all characters ~1.61% of the time.
FRAMEWORK SUPPORT - .NET Core 3 (and future platforms that support .NET Standard 2.1 or above) provides a cryptographically sound method RandomNumberGenerator.GetInt32() to generate a random integer within a desired range.
Unlike some of the alternatives presented, this one is cryptographically sound.
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey
{
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4*size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
public static string GetUniqueKeyOriginal_BIASED(int size)
{
char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
}
Based on a discussion of alternatives here and updated/modified based on the comments below.
Here's a small test harness that demonstrates the distribution of characters in the old and updated output. For a deep discussion of the analysis of randomness, check out 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")}%");
}
}
}
}
Update 7/25/2022
Based on a question in the comments, I was curious whether the distribution is truly random.
I'm not a statistician, but I could probably play one on TV. If an actual statistician wants to chime in, that would be most welcome.
There are 62 possible output values (A-Za-Z0-9) and int.MaxValue numbers used to pick an array index. int.MaxValue % 62 is 1, so one character will be selected 1-in-4 billionish times more often than the others. We could further reduce that selection bias by randomly rotating the array of output values prior to indexing.
A T-test or other statistical measure would be the best approach to determine whether there is bias in the output results, but that's not something I can accomplish on my lunch break, so instead I leave you with a modification of the above code that measures deviation from expectation. Note that it tends toward zero.
using System.Security.Cryptography;
using System.Text;
const int REPETITIONS = 1_000_000;
const int KEY_SIZE = 32;
int TASK_COUNT = Environment.ProcessorCount - 1;
var expectedPercentage = 100.0 / KeyGenerator.chars.Length;
var done = false;
var iterationNr = 1;
var totalRandomSymbols = 0L;
var grandTotalCounts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) grandTotalCounts.Add(ch, 0);
while (!done)
{
var experiments = Enumerable.Range(0, TASK_COUNT).Select(i => Task.Run(Experiment)).ToArray();
Task.WaitAll(experiments);
var totalCountsThisRun = experiments.SelectMany(e => e.Result)
.GroupBy(e => e.Key)
.Select(e => new { e.Key, Count = e.Select(_ => _.Value).Sum() })
.ToDictionary(e => e.Key, e => e.Count);
foreach (var ch in KeyGenerator.chars)
grandTotalCounts[ch] += totalCountsThisRun[ch];
var totalChars = grandTotalCounts.Values.Sum();
totalRandomSymbols += totalChars;
var distributionScores = KeyGenerator.chars.Select(ch =>
new
{
Symbol = ch,
OverUnder = (100.0 * grandTotalCounts[ch] / totalChars) - expectedPercentage
});
Console.WriteLine($"Iteration {iterationNr++}. Total random symbols: {totalRandomSymbols:N0}");
foreach (var chWithValue in distributionScores.OrderByDescending(c => c.OverUnder))
{
Console.WriteLine($"{chWithValue.Symbol}: {chWithValue.OverUnder:#.00000}%");
}
done = Console.KeyAvailable;
}
Dictionary<char, long> Experiment()
{
var counts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
{
var key = KeyGenerator.GetUniqueKey(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
}
return counts;
}
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4 * size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
}
Solution 1 - largest 'range' with most flexible length
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);
}
}
This solution has more range than using a GUID because a GUID has a couple of fixed bits that are always the same and therefore not random, for example the 13 character in hex is always "4" - at least in a version 6 GUID.
This solution also lets you generate a string of any length.
Solution 2 - One line of code - good for up to 22 characters
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
You can't generate strings as long as Solution 1 and the string doesn't have the same range due to fixed bits in GUID's, but in a lot of cases this will do the job.
Solution 3 - Slightly less code
Guid.NewGuid().ToString("n").Substring(0, 8);
Mostly keeping this here for historical purpose. It uses slightly less code, that though comes as the expense of having less range - because it uses hex instead of base64 it takes more characters to represent the same range compared the other solutions.
Which means more chance of collision - testing it with 100,000 iterations of 8 character strings generated one duplicate.
Here's an example that I stole from Sam Allen example at Dot Net Perls
If you only need 8 characters, then use Path.GetRandomFileName() in the System.IO namespace. Sam says using the "Path.GetRandomFileName method here is sometimes superior, because it uses RNGCryptoServiceProvider for better randomness. However, it is limited to 11 random characters."
GetRandomFileName always returns a 12 character string with a period at the 9th character. So you'll need to strip the period (since that's not random) and then take 8 characters from the string. Actually, you could just take the first 8 characters and not worry about the period.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS: thanks Sam
The main goals of my code are:
The distribution of strings is almost uniform (don't care about minor deviations, as long as they're small)
It outputs more than a few billion strings for each argument set. Generating an 8 character string (~47 bits of entropy) is meaningless if your PRNG only generates 2 billion (31 bits of entropy) different values.
It's secure, since I expect people to use this for passwords or other security tokens.
The first property is achieved by taking a 64 bit value modulo the alphabet size. For small alphabets (such as the 62 characters from the question) this leads to negligible bias. The second and third property are achieved by using RNGCryptoServiceProvider instead of 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);
}
The simplest:
public static string GetRandomAlphaNumeric()
{
return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}
You can get better performance if you hard code the char array and rely on System.Random:
public static string GetRandomAlphaNumeric()
{
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
If ever you worry the English alphabets can change sometime around and you might lose business, then you can avoid hard coding, but should perform slightly worse (comparable to Path.GetRandomFileName approach)
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);
}
The last two approaches looks better if you can make them an extension method on System.Random instance.
Just some performance comparisons of the various answers in this thread:
Methods & Setup
// 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);
}
Results
Tested in LinqPad. For string size of 10, generates:
from Linq = chdgmevhcy [10]
from Loop = gtnoaryhxr [10]
from Select = rsndbztyby [10]
from GenerateRandomString = owyefjjakj [10]
from SecureFastRandom = VzougLYHYP [10]
from SecureFastRandom-NoCache = oVQXNGmO1S [10]
And the performance numbers tend to vary slightly, very occasionally NonOptimized is actually faster, and sometimes ForLoop and GenerateRandomString switch who's in the lead.
LinqIsTheNewBlack (10000x) = 96762 ticks elapsed (9.6762 ms)
ForLoop (10000x) = 28970 ticks elapsed (2.897 ms)
ForLoopNonOptimized (10000x) = 33336 ticks elapsed (3.3336 ms)
Repeat (10000x) = 78547 ticks elapsed (7.8547 ms)
GenerateRandomString (10000x) = 27416 ticks elapsed (2.7416 ms)
SecureFastRandom (10000x) = 13176 ticks elapsed (5ms) lowest [Different machine]
SecureFastRandom-NoCache (10000x) = 39541 ticks elapsed (17ms) lowest [Different machine]
One line of code Membership.GeneratePassword() does the trick :)
Here is a demo for the same.
The code written by Eric J. is quite sloppy (it is quite clear that it is from 6 years ago... he probably wouldn't write that code today), and there are even some problems.
Unlike some of the alternatives presented, this one is cryptographically sound.
Untrue... There is a bias in the password (as written in a comment), bcdefgh are a little more probable than the others (the a isn't because by the GetNonZeroBytes it isn't generating bytes with a value of zero, so the bias for the a is balanced by it), so it isn't really cryptographically sound.
This should correct all the problems.
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);
}
}
My simple one line code works for me :)
string random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));
Response.Write(random.ToUpper());
Response.Write(random.ToLower());
To expand on this for any length string
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);
}
We also use custom string random but we implemented is as a string's helper so it provides some flexibility...
public static string Random(this string chars, int length = 8)
{
var randomString = new StringBuilder();
var random = new Random();
for (int i = 0; i < length; i++)
randomString.Append(chars[random.Next(chars.Length)]);
return randomString.ToString();
}
Usage
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();
or
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
A slightly cleaner version of DTB's solution.
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
return string.Join("", list);
Your style preferences may vary.
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());
}
Another option could be to use Linq and aggregate random chars into a stringbuilder.
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());
Question: Why should I waste my time using Enumerable.Range instead of typing in "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;
}
}
Answer: Magic strings are BAD. Did ANYONE notice there was no "I" in my string at the top? My mother taught me not to use magic strings for this very reason...
n.b. 1: As many others like #dtb said, don't use System.Random if you need cryptographic security...
n.b. 2: This answer isn't the most efficient or shortest, but I wanted the space to separate the answer from the question. The purpose of my answer is more to warn against magic strings than to provide a fancy innovative answer.
After reviewing the other answers and considering CodeInChaos' comments, along with CodeInChaos still biased (although less) answer, I thought a final ultimate cut and paste solution was needed. So while updating my answer I decided to go all out.
For an up to date version of this code, please visit the new Hg repository on Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom. I recommend you copy and paste the code from: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (make sure you click the Raw button to make it easier to copy and make sure you have the latest version, I think this link goes to a specific version of the code, not the latest).
Updated notes:
Relating to some other answers - If you know the length of the output, you don't need a StringBuilder, and when using ToCharArray, this creates and fills the array (you don't need to create an empty array first)
Relating to some other answers - You should use NextBytes, rather than getting one at a time for performance
Technically you could pin the byte array for faster access.. it's usually worth it when your iterating more than 6-8 times over a byte array. (Not done here)
Use of RNGCryptoServiceProvider for best randomness
Use of caching of a 1MB buffer of random data - benchmarking shows cached single bytes access speed is ~1000x faster - taking 9ms over 1MB vs 989ms for uncached.
Optimised rejection of bias zone within my new class.
End solution to question:
static char[] charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
for (var i = 0; i < Length; i++)
{
rName[i] = charSet[rBytes[i] % charSet.Length];
}
return new string(rName);
}
But you need my new (untested) class:
/// <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
}
}
}
For history - my older solution for this answer, used Random object:
private static char[] charSet =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
lock (rGen) //~20-50ns
{
rGen.NextBytes(rBytes);
for (int i = 0; i < Length; i++)
{
while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
rBytes[i] = rGen.NextByte();
rName[i] = charSet[rBytes[i] % charSet.Length];
}
}
return new string(rName);
}
Performance:
SecureFastRandom - First single run = ~9-33ms. Imperceptible. Ongoing: 5ms (sometimes it goes up to 13ms) over 10,000 iterations, With a single average iteration= 1.5 microseconds.. Note: Requires generally 2, but occasionally up to 8 cache refreshes - depends on how many single bytes exceed the bias zone
Random - First single run = ~0-1ms. Imperceptible. Ongoing: 5ms over 10,000 iterations. With a single average iteration= .5 microseconds.. About the same speed.
Also check out:
https://bitbucket.org/merarischroeder/number-range-with-no-bias/src
https://stackoverflow.com/a/45118325/887092
These links are another approach. Buffering could be added to this new code base, but most important was exploring different approaches to removing bias, and benchmarking the speeds and pros/cons.
Simplest and most flexible solution with cryptographic security (.NET Core 3.0+):
If you're using .NET Core 3.0 or above, you can use the new static GetInt32 method (which is cryptographically secure) on the RandomNumberGenerator class to generate a random index for a given character set and populate the result that way quite easily.
This approach is far simpler than, for instance, the one proposed in this answer; and it also provides complete flexibility since you can pass in any character set you want.
public static string GenerateRandomString(int length, IEnumerable<char> charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
{
var charArray = charSet.Distinct().ToArray();
char[] result = new char[length];
for (int i = 0; i < length; i++)
result[i] = charArray[RandomNumberGenerator.GetInt32(charArray.Length)];
return new string(result);
}
Usage:
string randomAlphanumericString = GenerateRandomString(length: 10);
A simple and highly secure way might be generating the cryptography Aes key.
public static string GenerateRandomString()
{
using Aes crypto = Aes.Create();
crypto.GenerateKey();
return Convert.ToBase64String(crypto.Key);
}
I was looking for a more specific answer, where I want to control the format of the random string and came across this post. For example: license plates (of cars) have a specific format (per country) and I wanted to created random license plates.
I decided to write my own extension method of Random for this. (this is in order to reuse the same Random object, as you could have doubles in multi-threading scenarios).
I created a gist (https://gist.github.com/SamVanhoutte/808845ca78b9c041e928), but will also copy the extension class here:
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)];
}
}
Now in one-liner flavour.
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());
}
Try to combine two parts: unique (sequence, counter or date ) and random
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;
using (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)
{
const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish to make the 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;
}
}
Tests:
[Test]
public void Generator_Should_BeUnigue1()
{
//Given
var loop = Enumerable.Range(0, 1000);
//When
var str = loop.Select(x=> RandomStringGenerator.Gen());
//Then
var distinct = str.Distinct();
Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
}
For both crypto & noncrypto, efficiently:
public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new Random().GenerateRandomString(length, charset);
public static string GenerateRandomString(this Random random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
RandomString(random.NextBytes, length, charset.ToCharArray());
public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
return crypto.GenerateRandomCryptoString(length, charset);
}
public static string GenerateRandomCryptoString(this RNGCryptoServiceProvider random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
RandomString(random.GetBytes, length, charset.ToCharArray());
private static string RandomString(Action<byte[]> fillRandomBuffer, int length, char[] charset)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), $"{nameof(length)} must be greater or equal to 0");
if (charset is null)
throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0)
throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
var maxIdx = charset.Length;
var chars = new char[length];
var randomBuffer = new byte[length * 4];
fillRandomBuffer(randomBuffer);
for (var i = 0; i < length; i++)
chars[i] = charset[BitConverter.ToUInt32(randomBuffer, i * 4) % maxIdx];
return new string(chars);
}
Using generators & LINQ. Not the fastest option (especially because it doesn't generate all the bytes in one go) but pretty neat & extensible:
private static readonly Random _random = new Random();
public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new string(_random.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
return new string(crypto.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
}
public static IEnumerable<char> RandomChars(this Func<uint, IEnumerable<uint>> randomGenerator, char[] charset)
{
if (charset is null)
throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0)
throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
return randomGenerator((uint)charset.Length).Select(r => charset[r]);
}
public static Func<uint, IEnumerable<uint>> GetGenerator(this Random random)
{
if (random is null)
throw new ArgumentNullException(nameof(random));
return GeneratorFunc_Inner;
IEnumerable<uint> GeneratorFunc_Inner(uint maxValue)
{
if (maxValue > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(maxValue));
return Generator_Inner();
IEnumerable<uint> Generator_Inner()
{
var randomBytes = new byte[4];
while (true)
{
random.NextBytes(randomBytes);
yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
}
}
}
}
public static Func<uint, IEnumerable<uint>> GetGenerator(this System.Security.Cryptography.RNGCryptoServiceProvider random)
{
if (random is null)
throw new ArgumentNullException(nameof(random));
return Generator_Inner;
IEnumerable<uint> Generator_Inner(uint maxValue)
{
var randomBytes = new byte[4];
while (true)
{
random.GetBytes(randomBytes);
yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
}
}
}
a simpler version using LINQ for only non-crypto strings:
private static readonly Random _random = new Random();
public static string RandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new string(_random.GenerateChars(charset).Take(length).ToArray());
public static IEnumerable<char> GenerateChars(this Random random, string charset)
{
if (charset is null) throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0) throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
return random.Generator(charset.Length).Select(r => charset[r]);
}
public static IEnumerable<int> Generator(this Random random, int maxValue)
{
if (random is null) throw new ArgumentNullException(nameof(random));
return Generator_Inner();
IEnumerable<int> Generator_Inner() { while (true) yield return random.Next(maxValue); }
}
Horrible, I know, but I just couldn't help myself:
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);
}
}
}
A solution that includes all alphabet characters and numbers which you can change as you wish:
public static string RandomString(int length)
{
Random rand = new Random();
string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return new string(Enumerable.Range(0,length)
.Select(_ => charbase[rand.Next(charbase.Length)])
.ToArray());
}
If you like one-liner methods ;)
public static Random rand = new Random();
public const string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
public static string RandomString(int length) =>
new string(Enumerable.Range(0,length).Select(_ => charbase[rand.Next(charbase.Length)]).ToArray());
A solution without using Random :
var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);
var randomStr = new string(chars.SelectMany(str => str)
.OrderBy(c => Guid.NewGuid())
.Take(8).ToArray());
Here is a variant of Eric J's solution, i.e. cryptographically sound, for 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();
}
If performance matters (especially when length is high):
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();
}
I know this one is not the best way. But you can try this.
string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
I don't know how cryptographically sound this is, but it's more readable and concise than the more intricate solutions by far (imo), and it should be more "random" than System.Random-based solutions.
return alphabet
.OrderBy(c => Guid.NewGuid())
.Take(strLength)
.Aggregate(
new StringBuilder(),
(builder, c) => builder.Append(c))
.ToString();
I can't decide if I think this version or the next one is "prettier", but they give the exact same results:
return new string(alphabet
.OrderBy(o => Guid.NewGuid())
.Take(strLength)
.ToArray());
Granted, it isn't optimized for speed, so if it's mission critical to generate millions of random strings every second, try another one!
NOTE: This solution doesn't allow for repetitions of symbols in the alphabet, and the alphabet MUST be of equal or greater size than the output string, making this approach less desirable in some circumstances, it all depends on your use-case.
If your values are not completely random, but in fact may depend on something - you may compute an md5 or sha1 hash of that 'somwthing' and then truncate it to whatever length you want.
Also you may generate and truncate a guid.
Related
I am trying to create a helper function that prepends x amounts of zeroes to the integer where x is the difference between a given length and the number of characters in that int.
For example:
// data = 9573132375
// length = 15
// should return (15 - 10) 000009573132375
public string ModulateData(long data, int length)
{
if (data.ToString().Length <= length)
{
int noToPrepend = length - data.ToString().Length;
string leadingzeros = "";
for (int i = 0; i < noToPrepend; i++)
{
leadingzeros += "0";
}
return leadingzeros + data.ToString();
}
return "";
}
I can only think of converting the data variable to a string, prepending 0s, and converting the string back to an integer later. However, I am concerned about the performance of this approach, as this method will be part of my core design, it needs to be as performant as possible. Is there a faster way this can be done? Are there any performance penalties with my current code?
EDIT
I am communicating the data variable as request data to a machine that accepts bytes, so I will be using this function as follows -
string requestdata = "";
requestdata += ModulateData(x,20);
requestdata += ModulateData(y,5);
requestdata += ModulateData(z,10); // Could be about 20 or so lines like this.
After that I will convert requestdata to a byte array in Hex Format. I already have working code for that.
Shorter version:
public static string ModulateData(long data, int length) =>
(data.ToString().Length <= length)
? $"{new string('0', length - data.ToString().Length)}{data}"
: string.Empty;
And more debuggable version, if you want:
public static string ModulateData(long data, int length)
{
var dataAsText = data.ToString();
if (dataAsText.Length <= length)
{
int noToPrepend = length - dataAsText.Length;
var leadingZeros = new string('0', noToPrepend);
return $"{leadingZeros}{dataAsText}";
}
return string.Empty;
}
And a usage:
// to make this more optimal you can calculate the StringBuilder initial capacity here,
// by taking into account possible characters length output
var requestData = new StringBuilder();
requestData.Append(ModulateData(x, 20));
requestData.Append(ModulateData(y, 5));
requestData.Append(ModulateData(z, 10));
I have a need to do very quick prefix "sql like" searches over a hundreds of thousands of keys. I have tried doing performance tests using a SortedList, a Dictionary, and a SortedDictionary, which I do like so :
var dictionary = new Dictionary<string, object>();
// add a million random strings
var results = dictionary.Where(x=>x.Key.StartsWith(prefix));
I find that that they all take a long time, Dictionary is the fastest, and SortedDictionary the slowest.
Then I tried a Trie implementation from http://www.codeproject.com/Articles/640998/NET-Data-Structures-for-Prefix-String-Search-and-S which is a magnitude faster, ie. milliseconds instead of seconds.
So my question is, is there no .NET collection I can use for the said requirement? I would have assumed that this would be a common requirement.
My basic test :
class Program
{
static readonly Dictionary<string, object> dictionary = new Dictionary<string, object>();
static Trie<object> trie = new Trie<object>();
static void Main(string[] args)
{
var random = new Random();
for (var i = 0; i < 100000; i++)
{
var randomstring = RandomString(random, 7);
dictionary.Add(randomstring, null);
trie.Add(randomstring, null);
}
var lookups = new string[10000];
for (var i = 0; i < lookups.Length; i++)
{
lookups[i] = RandomString(random, 3);
}
// compare searching
var sw = new Stopwatch();
sw.Start();
foreach (var lookup in lookups)
{
var exists = dictionary.Any(k => k.Key.StartsWith(lookup));
}
sw.Stop();
Console.WriteLine("dictionary.Any(k => k.Key.StartsWith(randomstring)) took : {0} ms", sw.ElapsedMilliseconds);
// test other collections
sw.Restart();
foreach (var lookup in lookups)
{
var exists = trie.Retrieve(lookup).Any();
}
sw.Stop();
Console.WriteLine("trie.Retrieve(lookup) took : {0} ms", sw.ElapsedMilliseconds);
Console.ReadKey();
}
public static string RandomString(Random random,int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
}
Results:
dictionary.Any(k => k.Key.StartsWith(randomstring)) took : 80990 ms
trie.Retrieve(lookup) took : 115 ms
If sorting matters, try to use a SortedList instead of SortedDictionary. They both have the same functionality but they are implemented differently. SortedList is faster when you want to enumerate the elements (and you can access the elements by index), and SortedDictionary is faster if there are a lot of elements and you want to insert a new element in the middle of the collection.
So try this:
var sortedList = new SortedList<string, object>();
// populate list...
sortedList.Keys.Any(k => k.StartsWith(lookup));
If you have a million elements, but you don't want to re-order them once the dictionary is populated, you can combine their advantages: populate a SortedDictionary with the random elements, and then create a new List<KeyValuePair<,>> or SortedList<,> from that.
So, after little test I found something close enought with usage BinarySearch only Cons is that you have to sort keys from a to z. But the biggest the list, the slower it will be so Ternary Search is fastest from all you can actualy found with binary pc architecture.
Method: (Credits shoult go to #Guffa)
public static int BinarySearchStartsWith(List<string> words, string prefix, int min, int max)
{
while (max >= min)
{
var mid = (min + max) / 2;
var comp = string.CompareOrdinal(words[mid].Substring(0, prefix.Length), prefix);
if (comp >= 0)
{
if (comp > 0)
max = mid - 1;
else
return mid;
}
else
min = mid + 1;
}
return -1;
}
and test implementation
var keysToList = dictionary.Keys.OrderBy(q => q).ToList();
sw = new Stopwatch();
sw.Start();
foreach (var lookup in lookups)
{
bool exist = BinarySearchStartsWith(keysToList, lookup, 0, keysToList.Count - 1)!= -1
}
sw.Stop();
If you can sort the keys once and then use them repeatedly to look up the prefixes, then you can use a binary search to speed things up.
To get the maximum performance, I shall use two arrays, once for keys and one for values, and use the overload of Array.Sort() which sorts a main and an adjunct array.
Then you can use Array.BinarySearch() to search for the nearest key which starts with a given prefix, and return the indices for those that match.
When I try it, it seems to only take around 0.003ms per check if there are one or more matching prefixes.
Here's a runnable console application to demonstrate (remember to do your timings on a RELEASE build):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
int count = 1000000;
object obj = new object();
var keys = new string[count];
var values = new object[count];
for (int i = 0; i < count; ++i)
{
keys[i] = randomString(5, 16);
values[i] = obj;
}
// Sort key array and value arrays in tandem to keep the relation between keys and values.
Array.Sort(keys, values);
// Now you can use StartsWith() to return the indices of strings in keys[]
// that start with a specific string. The indices can be used to look up the
// corresponding values in values[].
Console.WriteLine("Count of ZZ = " + StartsWith(keys, "ZZ").Count());
// Test a load of times with 1000 random prefixes.
var prefixes = new string[1000];
for (int i = 0; i < 1000; ++i)
prefixes[i] = randomString(1, 8);
var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; ++i)
for (int j = 0; j < 1000; ++j)
StartsWith(keys, prefixes[j]).Any();
Console.WriteLine("1,000,000 checks took {0} for {1} ms each.", sw.Elapsed, sw.ElapsedMilliseconds/1000000.0);
}
public static IEnumerable<int> StartsWith(string[] array, string prefix)
{
int index = Array.BinarySearch(array, prefix);
if (index < 0)
index = ~index;
// We might have landed partway through a set of matches, so find the first match.
if (index < array.Length)
while ((index > 0) && array[index-1].StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
--index;
while ((index < array.Length) && array[index].StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
yield return index++;
}
static string randomString(int minLength, int maxLength)
{
int length = rng.Next(minLength, maxLength);
const string CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Repeat(CHARS, length)
.Select(s => s[rng.Next(s.Length)]).ToArray());
}
static readonly Random rng = new Random(12345);
}
}
How can I generate a random 8 character alphanumeric string in C#?
I heard LINQ is the new black, so here's my attempt using 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());
}
(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)
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);
Not as elegant as the Linq solution.
(Note: The use of the Random class makes this unsuitable for anything security related, such as creating passwords or tokens. Use the RNGCryptoServiceProvider class if you need a strong random number generator.)
UPDATED for .NET 6. RNGCryptoServiceProvider is marked as obsolete. Instead, call RandomNumberGenerator.Create(). The code in the answer has been updated accordingly.
UPDATED based on comments. The original implementation generated a-h ~1.95% of the time and the remaining characters ~1.56% of the time. The update generates all characters ~1.61% of the time.
FRAMEWORK SUPPORT - .NET Core 3 (and future platforms that support .NET Standard 2.1 or above) provides a cryptographically sound method RandomNumberGenerator.GetInt32() to generate a random integer within a desired range.
Unlike some of the alternatives presented, this one is cryptographically sound.
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey
{
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4*size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
public static string GetUniqueKeyOriginal_BIASED(int size)
{
char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
}
Based on a discussion of alternatives here and updated/modified based on the comments below.
Here's a small test harness that demonstrates the distribution of characters in the old and updated output. For a deep discussion of the analysis of randomness, check out 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")}%");
}
}
}
}
Update 7/25/2022
Based on a question in the comments, I was curious whether the distribution is truly random.
I'm not a statistician, but I could probably play one on TV. If an actual statistician wants to chime in, that would be most welcome.
There are 62 possible output values (A-Za-Z0-9) and int.MaxValue numbers used to pick an array index. int.MaxValue % 62 is 1, so one character will be selected 1-in-4 billionish times more often than the others. We could further reduce that selection bias by randomly rotating the array of output values prior to indexing.
A T-test or other statistical measure would be the best approach to determine whether there is bias in the output results, but that's not something I can accomplish on my lunch break, so instead I leave you with a modification of the above code that measures deviation from expectation. Note that it tends toward zero.
using System.Security.Cryptography;
using System.Text;
const int REPETITIONS = 1_000_000;
const int KEY_SIZE = 32;
int TASK_COUNT = Environment.ProcessorCount - 1;
var expectedPercentage = 100.0 / KeyGenerator.chars.Length;
var done = false;
var iterationNr = 1;
var totalRandomSymbols = 0L;
var grandTotalCounts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) grandTotalCounts.Add(ch, 0);
while (!done)
{
var experiments = Enumerable.Range(0, TASK_COUNT).Select(i => Task.Run(Experiment)).ToArray();
Task.WaitAll(experiments);
var totalCountsThisRun = experiments.SelectMany(e => e.Result)
.GroupBy(e => e.Key)
.Select(e => new { e.Key, Count = e.Select(_ => _.Value).Sum() })
.ToDictionary(e => e.Key, e => e.Count);
foreach (var ch in KeyGenerator.chars)
grandTotalCounts[ch] += totalCountsThisRun[ch];
var totalChars = grandTotalCounts.Values.Sum();
totalRandomSymbols += totalChars;
var distributionScores = KeyGenerator.chars.Select(ch =>
new
{
Symbol = ch,
OverUnder = (100.0 * grandTotalCounts[ch] / totalChars) - expectedPercentage
});
Console.WriteLine($"Iteration {iterationNr++}. Total random symbols: {totalRandomSymbols:N0}");
foreach (var chWithValue in distributionScores.OrderByDescending(c => c.OverUnder))
{
Console.WriteLine($"{chWithValue.Symbol}: {chWithValue.OverUnder:#.00000}%");
}
done = Console.KeyAvailable;
}
Dictionary<char, long> Experiment()
{
var counts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
{
var key = KeyGenerator.GetUniqueKey(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
}
return counts;
}
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4 * size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
}
Solution 1 - largest 'range' with most flexible length
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);
}
}
This solution has more range than using a GUID because a GUID has a couple of fixed bits that are always the same and therefore not random, for example the 13 character in hex is always "4" - at least in a version 6 GUID.
This solution also lets you generate a string of any length.
Solution 2 - One line of code - good for up to 22 characters
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
You can't generate strings as long as Solution 1 and the string doesn't have the same range due to fixed bits in GUID's, but in a lot of cases this will do the job.
Solution 3 - Slightly less code
Guid.NewGuid().ToString("n").Substring(0, 8);
Mostly keeping this here for historical purpose. It uses slightly less code, that though comes as the expense of having less range - because it uses hex instead of base64 it takes more characters to represent the same range compared the other solutions.
Which means more chance of collision - testing it with 100,000 iterations of 8 character strings generated one duplicate.
Here's an example that I stole from Sam Allen example at Dot Net Perls
If you only need 8 characters, then use Path.GetRandomFileName() in the System.IO namespace. Sam says using the "Path.GetRandomFileName method here is sometimes superior, because it uses RNGCryptoServiceProvider for better randomness. However, it is limited to 11 random characters."
GetRandomFileName always returns a 12 character string with a period at the 9th character. So you'll need to strip the period (since that's not random) and then take 8 characters from the string. Actually, you could just take the first 8 characters and not worry about the period.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS: thanks Sam
The main goals of my code are:
The distribution of strings is almost uniform (don't care about minor deviations, as long as they're small)
It outputs more than a few billion strings for each argument set. Generating an 8 character string (~47 bits of entropy) is meaningless if your PRNG only generates 2 billion (31 bits of entropy) different values.
It's secure, since I expect people to use this for passwords or other security tokens.
The first property is achieved by taking a 64 bit value modulo the alphabet size. For small alphabets (such as the 62 characters from the question) this leads to negligible bias. The second and third property are achieved by using RNGCryptoServiceProvider instead of 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);
}
The simplest:
public static string GetRandomAlphaNumeric()
{
return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}
You can get better performance if you hard code the char array and rely on System.Random:
public static string GetRandomAlphaNumeric()
{
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
If ever you worry the English alphabets can change sometime around and you might lose business, then you can avoid hard coding, but should perform slightly worse (comparable to Path.GetRandomFileName approach)
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);
}
The last two approaches looks better if you can make them an extension method on System.Random instance.
Just some performance comparisons of the various answers in this thread:
Methods & Setup
// 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);
}
Results
Tested in LinqPad. For string size of 10, generates:
from Linq = chdgmevhcy [10]
from Loop = gtnoaryhxr [10]
from Select = rsndbztyby [10]
from GenerateRandomString = owyefjjakj [10]
from SecureFastRandom = VzougLYHYP [10]
from SecureFastRandom-NoCache = oVQXNGmO1S [10]
And the performance numbers tend to vary slightly, very occasionally NonOptimized is actually faster, and sometimes ForLoop and GenerateRandomString switch who's in the lead.
LinqIsTheNewBlack (10000x) = 96762 ticks elapsed (9.6762 ms)
ForLoop (10000x) = 28970 ticks elapsed (2.897 ms)
ForLoopNonOptimized (10000x) = 33336 ticks elapsed (3.3336 ms)
Repeat (10000x) = 78547 ticks elapsed (7.8547 ms)
GenerateRandomString (10000x) = 27416 ticks elapsed (2.7416 ms)
SecureFastRandom (10000x) = 13176 ticks elapsed (5ms) lowest [Different machine]
SecureFastRandom-NoCache (10000x) = 39541 ticks elapsed (17ms) lowest [Different machine]
One line of code Membership.GeneratePassword() does the trick :)
Here is a demo for the same.
The code written by Eric J. is quite sloppy (it is quite clear that it is from 6 years ago... he probably wouldn't write that code today), and there are even some problems.
Unlike some of the alternatives presented, this one is cryptographically sound.
Untrue... There is a bias in the password (as written in a comment), bcdefgh are a little more probable than the others (the a isn't because by the GetNonZeroBytes it isn't generating bytes with a value of zero, so the bias for the a is balanced by it), so it isn't really cryptographically sound.
This should correct all the problems.
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);
}
}
My simple one line code works for me :)
string random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));
Response.Write(random.ToUpper());
Response.Write(random.ToLower());
To expand on this for any length string
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);
}
We also use custom string random but we implemented is as a string's helper so it provides some flexibility...
public static string Random(this string chars, int length = 8)
{
var randomString = new StringBuilder();
var random = new Random();
for (int i = 0; i < length; i++)
randomString.Append(chars[random.Next(chars.Length)]);
return randomString.ToString();
}
Usage
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();
or
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
A slightly cleaner version of DTB's solution.
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
return string.Join("", list);
Your style preferences may vary.
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());
}
Another option could be to use Linq and aggregate random chars into a stringbuilder.
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());
Question: Why should I waste my time using Enumerable.Range instead of typing in "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;
}
}
Answer: Magic strings are BAD. Did ANYONE notice there was no "I" in my string at the top? My mother taught me not to use magic strings for this very reason...
n.b. 1: As many others like #dtb said, don't use System.Random if you need cryptographic security...
n.b. 2: This answer isn't the most efficient or shortest, but I wanted the space to separate the answer from the question. The purpose of my answer is more to warn against magic strings than to provide a fancy innovative answer.
After reviewing the other answers and considering CodeInChaos' comments, along with CodeInChaos still biased (although less) answer, I thought a final ultimate cut and paste solution was needed. So while updating my answer I decided to go all out.
For an up to date version of this code, please visit the new Hg repository on Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom. I recommend you copy and paste the code from: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (make sure you click the Raw button to make it easier to copy and make sure you have the latest version, I think this link goes to a specific version of the code, not the latest).
Updated notes:
Relating to some other answers - If you know the length of the output, you don't need a StringBuilder, and when using ToCharArray, this creates and fills the array (you don't need to create an empty array first)
Relating to some other answers - You should use NextBytes, rather than getting one at a time for performance
Technically you could pin the byte array for faster access.. it's usually worth it when your iterating more than 6-8 times over a byte array. (Not done here)
Use of RNGCryptoServiceProvider for best randomness
Use of caching of a 1MB buffer of random data - benchmarking shows cached single bytes access speed is ~1000x faster - taking 9ms over 1MB vs 989ms for uncached.
Optimised rejection of bias zone within my new class.
End solution to question:
static char[] charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
for (var i = 0; i < Length; i++)
{
rName[i] = charSet[rBytes[i] % charSet.Length];
}
return new string(rName);
}
But you need my new (untested) class:
/// <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
}
}
}
For history - my older solution for this answer, used Random object:
private static char[] charSet =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
lock (rGen) //~20-50ns
{
rGen.NextBytes(rBytes);
for (int i = 0; i < Length; i++)
{
while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
rBytes[i] = rGen.NextByte();
rName[i] = charSet[rBytes[i] % charSet.Length];
}
}
return new string(rName);
}
Performance:
SecureFastRandom - First single run = ~9-33ms. Imperceptible. Ongoing: 5ms (sometimes it goes up to 13ms) over 10,000 iterations, With a single average iteration= 1.5 microseconds.. Note: Requires generally 2, but occasionally up to 8 cache refreshes - depends on how many single bytes exceed the bias zone
Random - First single run = ~0-1ms. Imperceptible. Ongoing: 5ms over 10,000 iterations. With a single average iteration= .5 microseconds.. About the same speed.
Also check out:
https://bitbucket.org/merarischroeder/number-range-with-no-bias/src
https://stackoverflow.com/a/45118325/887092
These links are another approach. Buffering could be added to this new code base, but most important was exploring different approaches to removing bias, and benchmarking the speeds and pros/cons.
Simplest and most flexible solution with cryptographic security (.NET Core 3.0+):
If you're using .NET Core 3.0 or above, you can use the new static GetInt32 method (which is cryptographically secure) on the RandomNumberGenerator class to generate a random index for a given character set and populate the result that way quite easily.
This approach is far simpler than, for instance, the one proposed in this answer; and it also provides complete flexibility since you can pass in any character set you want.
public static string GenerateRandomString(int length, IEnumerable<char> charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
{
var charArray = charSet.Distinct().ToArray();
char[] result = new char[length];
for (int i = 0; i < length; i++)
result[i] = charArray[RandomNumberGenerator.GetInt32(charArray.Length)];
return new string(result);
}
Usage:
string randomAlphanumericString = GenerateRandomString(length: 10);
A simple and highly secure way might be generating the cryptography Aes key.
public static string GenerateRandomString()
{
using Aes crypto = Aes.Create();
crypto.GenerateKey();
return Convert.ToBase64String(crypto.Key);
}
I was looking for a more specific answer, where I want to control the format of the random string and came across this post. For example: license plates (of cars) have a specific format (per country) and I wanted to created random license plates.
I decided to write my own extension method of Random for this. (this is in order to reuse the same Random object, as you could have doubles in multi-threading scenarios).
I created a gist (https://gist.github.com/SamVanhoutte/808845ca78b9c041e928), but will also copy the extension class here:
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)];
}
}
Now in one-liner flavour.
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());
}
Try to combine two parts: unique (sequence, counter or date ) and random
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;
using (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)
{
const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish to make the 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;
}
}
Tests:
[Test]
public void Generator_Should_BeUnigue1()
{
//Given
var loop = Enumerable.Range(0, 1000);
//When
var str = loop.Select(x=> RandomStringGenerator.Gen());
//Then
var distinct = str.Distinct();
Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
}
For both crypto & noncrypto, efficiently:
public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new Random().GenerateRandomString(length, charset);
public static string GenerateRandomString(this Random random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
RandomString(random.NextBytes, length, charset.ToCharArray());
public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
return crypto.GenerateRandomCryptoString(length, charset);
}
public static string GenerateRandomCryptoString(this RNGCryptoServiceProvider random, int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
RandomString(random.GetBytes, length, charset.ToCharArray());
private static string RandomString(Action<byte[]> fillRandomBuffer, int length, char[] charset)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), $"{nameof(length)} must be greater or equal to 0");
if (charset is null)
throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0)
throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
var maxIdx = charset.Length;
var chars = new char[length];
var randomBuffer = new byte[length * 4];
fillRandomBuffer(randomBuffer);
for (var i = 0; i < length; i++)
chars[i] = charset[BitConverter.ToUInt32(randomBuffer, i * 4) % maxIdx];
return new string(chars);
}
Using generators & LINQ. Not the fastest option (especially because it doesn't generate all the bytes in one go) but pretty neat & extensible:
private static readonly Random _random = new Random();
public static string GenerateRandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new string(_random.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
public static string GenerateRandomCryptoString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider())
return new string(crypto.GetGenerator().RandomChars(charset.ToCharArray()).Take(length).ToArray());
}
public static IEnumerable<char> RandomChars(this Func<uint, IEnumerable<uint>> randomGenerator, char[] charset)
{
if (charset is null)
throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0)
throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
return randomGenerator((uint)charset.Length).Select(r => charset[r]);
}
public static Func<uint, IEnumerable<uint>> GetGenerator(this Random random)
{
if (random is null)
throw new ArgumentNullException(nameof(random));
return GeneratorFunc_Inner;
IEnumerable<uint> GeneratorFunc_Inner(uint maxValue)
{
if (maxValue > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(maxValue));
return Generator_Inner();
IEnumerable<uint> Generator_Inner()
{
var randomBytes = new byte[4];
while (true)
{
random.NextBytes(randomBytes);
yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
}
}
}
}
public static Func<uint, IEnumerable<uint>> GetGenerator(this System.Security.Cryptography.RNGCryptoServiceProvider random)
{
if (random is null)
throw new ArgumentNullException(nameof(random));
return Generator_Inner;
IEnumerable<uint> Generator_Inner(uint maxValue)
{
var randomBytes = new byte[4];
while (true)
{
random.GetBytes(randomBytes);
yield return BitConverter.ToUInt32(randomBytes, 0) % maxValue;
}
}
}
a simpler version using LINQ for only non-crypto strings:
private static readonly Random _random = new Random();
public static string RandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") =>
new string(_random.GenerateChars(charset).Take(length).ToArray());
public static IEnumerable<char> GenerateChars(this Random random, string charset)
{
if (charset is null) throw new ArgumentNullException(nameof(charset));
if (charset.Length == 0) throw new ArgumentException($"{nameof(charset)} must contain at least 1 character", nameof(charset));
return random.Generator(charset.Length).Select(r => charset[r]);
}
public static IEnumerable<int> Generator(this Random random, int maxValue)
{
if (random is null) throw new ArgumentNullException(nameof(random));
return Generator_Inner();
IEnumerable<int> Generator_Inner() { while (true) yield return random.Next(maxValue); }
}
Horrible, I know, but I just couldn't help myself:
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);
}
}
}
A solution that includes all alphabet characters and numbers which you can change as you wish:
public static string RandomString(int length)
{
Random rand = new Random();
string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return new string(Enumerable.Range(0,length)
.Select(_ => charbase[rand.Next(charbase.Length)])
.ToArray());
}
If you like one-liner methods ;)
public static Random rand = new Random();
public const string charbase = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
public static string RandomString(int length) =>
new string(Enumerable.Range(0,length).Select(_ => charbase[rand.Next(charbase.Length)]).ToArray());
A solution without using Random :
var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);
var randomStr = new string(chars.SelectMany(str => str)
.OrderBy(c => Guid.NewGuid())
.Take(8).ToArray());
Here is a variant of Eric J's solution, i.e. cryptographically sound, for 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();
}
If performance matters (especially when length is high):
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();
}
I know this one is not the best way. But you can try this.
string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
I don't know how cryptographically sound this is, but it's more readable and concise than the more intricate solutions by far (imo), and it should be more "random" than System.Random-based solutions.
return alphabet
.OrderBy(c => Guid.NewGuid())
.Take(strLength)
.Aggregate(
new StringBuilder(),
(builder, c) => builder.Append(c))
.ToString();
I can't decide if I think this version or the next one is "prettier", but they give the exact same results:
return new string(alphabet
.OrderBy(o => Guid.NewGuid())
.Take(strLength)
.ToArray());
Granted, it isn't optimized for speed, so if it's mission critical to generate millions of random strings every second, try another one!
NOTE: This solution doesn't allow for repetitions of symbols in the alphabet, and the alphabet MUST be of equal or greater size than the output string, making this approach less desirable in some circumstances, it all depends on your use-case.
If your values are not completely random, but in fact may depend on something - you may compute an md5 or sha1 hash of that 'somwthing' and then truncate it to whatever length you want.
Also you may generate and truncate a guid.
I need a unique identifier in .NET (cannot use GUID as it is too long for this case).
Do people think that the algorithm used here is a good candidate or do you have any other suggestions?
This one a good one - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
and also here
YouTube-like GUID
You could use Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
That generates a string like E1HKfn68Pkms5zsZsvKONw==. Since a GUID is
always 128 bits, you can omit the == that you know will always be
present at the end and that will give you a 22 character string. This
isn't as short as YouTube though.
I use a similar approach as Dor Cohen's but removing some special characters:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
This will output just alphanumeric characters. The UIDs are not guaranteed to have always the same length. Here is a sample run:
vmKo0zws8k28fR4V4Hgmw
TKbhS0G2V0KqtpHOU8e6Ug
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg
CQUg1lXIXkWG8KDFy7z6Ow
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");
Keep a baseline date (which in this case is 1st Jan 2016) from when you will start generating these ids. This will make your ids smaller.
Generated Number: 3af3c14996e54
Simple usable package. I use it for temporal request id generator.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Uses System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(from the github page)
If you want to control the type of id generated by specifying whether you want numbers, special characters and the length, call the Generate method and pass three parameters, the first a boolean stating whether you want numbers, the second a boolean stating whether you want special characters, the last a number indicating your length preference.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
As far as I know, just stripping off a part of a GUID isn't guaranteed to be unique - in fact, it's far from being unique.
The shortest thing that I know that guarantees global uniqueness is featured in this blog post by Jeff Atwood. In the linked post, he discusses multiple ways to shorten a GUID, and in the end gets it down to 20 bytes via Ascii85 encoding.
However, if you absolutely need a solution that's no longer than 15 bytes, I'm afraid you have no other choice than to use something which is not guaranteed to be globally unique.
For my local app I'm using this time based approach:
/// <summary>
/// Returns all ticks, milliseconds or seconds since 1970.
///
/// 1 tick = 100 nanoseconds
///
/// Samples:
///
/// Return unit value decimal length value hex length
/// --------------------------------------------------------------------------
/// ticks 14094017407993061 17 3212786FA068F0 14
/// milliseconds 1409397614940 13 148271D0BC5 11
/// seconds 1409397492 10 5401D2AE 8
///
/// </summary>
public static string TickIdGet(bool getSecondsNotTicks, bool getMillisecondsNotTicks, bool getHexValue)
{
string id = string.Empty;
DateTime historicalDate = new DateTime(1970, 1, 1, 0, 0, 0);
if (getSecondsNotTicks || getMillisecondsNotTicks)
{
TimeSpan spanTillNow = DateTime.UtcNow.Subtract(historicalDate);
if (getSecondsNotTicks)
id = String.Format("{0:0}", spanTillNow.TotalSeconds);
else
id = String.Format("{0:0}", spanTillNow.TotalMilliseconds);
}
else
{
long ticksTillNow = DateTime.UtcNow.Ticks - historicalDate.Ticks;
id = ticksTillNow.ToString();
}
if (getHexValue)
id = long.Parse(id).ToString("X");
return id;
}
IDENTITY values should be unique in a database, but you should be aware of the limitations... for example, it makes bulk data inserts basically impossible which will slow you down if you're working with a very large number of records.
You may also be able to use a date/time value. I've seen several databases where they use the date/time to be the PK, and while it's not super clean - it works. If you control the inserts, you can effectively guarantee that the values will be unique in code.
Just in case merely removing hyphens will do for anyone:
Guid.NewGuid().ToString("n")
This generates perfectly unique strings of 32 characters:
5db4cee3bfd8436395d37fca2d48d5b3
82fac271c76148a3a0667c00a5da990d
here my solution, is not safe for concurrency, no more of 1000 GUID's per seconds and thread safe.
public static class Extensors
{
private static object _lockGuidObject;
public static string GetGuid()
{
if (_lockGuidObject == null)
_lockGuidObject = new object();
lock (_lockGuidObject)
{
Thread.Sleep(1);
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var epochLong = Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);
return epochLong.DecimalToArbitrarySystem(36);
}
}
/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(this long decimalNumber, int radix)
{
const int BitsInLong = 64;
const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (radix < 2 || radix > Digits.Length)
throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());
if (decimalNumber == 0)
return "0";
int index = BitsInLong - 1;
long currentNumber = Math.Abs(decimalNumber);
char[] charArray = new char[BitsInLong];
while (currentNumber != 0)
{
int remainder = (int)(currentNumber % radix);
charArray[index--] = Digits[remainder];
currentNumber = currentNumber / radix;
}
string result = new String(charArray, index + 1, BitsInLong - index - 1);
if (decimalNumber < 0)
{
result = "-" + result;
}
return result;
}
code not optimized, just sample!.
If your app dont have a few MILLIION people, using that generate short unique string at the SAME MILLISECOND, you can think about using below function.
private static readonly Object obj = new Object();
private static readonly Random random = new Random();
private string CreateShortUniqueString()
{
string strDate = DateTime.Now.ToString("yyyyMMddhhmmssfff");
string randomString ;
lock (obj)
{
randomString = RandomString(3);
}
return strDate + randomString; // 16 charater
}
private string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
var random = new Random();
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
change yyyy to yy if you just need to use your app in next 99 year.
Update 20160511: Correct Random function
- Add Lock object
- Move random variable out of RandomString function
Ref
Based on some others, here is my solution which provides a different encoded guid which is URL (and Docker) safe and does not loose any information:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Example outputs are:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
public static string ToTinyUuid(this Guid guid)
{
return Convert.ToBase64String(guid.ToByteArray())[0..^2] // remove trailing == padding
.Replace('+', '-') // escape (for filepath)
.Replace('/', '_'); // escape (for filepath)
}
Usage
Guid.NewGuid().ToTinyUuid()
It's not rocket science to convert back, so I'll leave you that much.
Here's my small method to generate a random and short unique id. Uses a cryptographic rng for secure random number generation. Add whatever characters you need to the chars string.
using System;
using System.Security.Cryptography;
// ...
private string GenerateRandomId(int length)
{
string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] outputChars = new char[length];
using RandomNumberGenerator rng = RandomNumberGenerator.Create();
int minIndex = 0;
int maxIndexExclusive = charset.Length;
int diff = maxIndexExclusive - minIndex;
long upperBound = uint.MaxValue / diff * diff;
byte[] randomBuffer = new byte[sizeof(int)];
for (int i = 0; i < outputChars.Length; i++)
{
// Generate a fair, random number between minIndex and maxIndex
uint randomUInt;
do
{
rng.GetBytes(randomBuffer);
randomUInt = BitConverter.ToUInt32(randomBuffer, 0);
}
while (randomUInt >= upperBound);
int charIndex = (int)(randomUInt % diff);
// Set output character based on random index
outputChars[i] = charset[charIndex];
}
return new string(outputChars);
}
This works by scaling a random integer down to the range of charset indices, and accounts for the edge case where the random number is the absolute upper bound by rerolling for a new integer.
This solution produces fair and evenly distributed output, tested with a 1,000,000 character long output showing no obvious biases:
string output = GenerateRandomId(1_000_000);
var tally = output.GroupBy(c => c).OrderBy(g => g.Key).Select(g => (g.Key, g.Count())).ToArray();
int average = (int)(tally.Aggregate(new BigInteger(0), (b, t) => {b += t.Item2; return b;}, b => b) / tally.Count());
int max = tally.Max(g => g.Item2);
int min = tally.Min(g => g.Item2);
Console.WriteLine($"Avg: {average}");
Console.WriteLine($"Max: {max}");
Console.WriteLine($"Min: {min}");
foreach((char key, int count) in tally) {
Console.WriteLine($"{key}: {count}");
}
Output:
Avg: 27777
Max: 28163
Min: 27341
0: 28081
1: 27773
...
Z: 27725
I know it's quite far from posted date... :)
I have a generator which produces only 9 Hexa characters, eg: C9D6F7FF3, C9D6FB52C
public class SlimHexIdGenerator : IIdGenerator
{
private readonly DateTime _baseDate = new DateTime(2016, 1, 1);
private readonly IDictionary<long, IList<long>> _cache = new Dictionary<long, IList<long>>();
public string NewId()
{
var now = DateTime.Now.ToString("HHmmssfff");
var daysDiff = (DateTime.Today - _baseDate).Days;
var current = long.Parse(string.Format("{0}{1}", daysDiff, now));
return IdGeneratorHelper.NewId(_cache, current);
}
}
static class IdGeneratorHelper
{
public static string NewId(IDictionary<long, IList<long>> cache, long current)
{
if (cache.Any() && cache.Keys.Max() < current)
{
cache.Clear();
}
if (!cache.Any())
{
cache.Add(current, new List<long>());
}
string secondPart;
if (cache[current].Any())
{
var maxValue = cache[current].Max();
cache[current].Add(maxValue + 1);
secondPart = maxValue.ToString(CultureInfo.InvariantCulture);
}
else
{
cache[current].Add(0);
secondPart = string.Empty;
}
var nextValueFormatted = string.Format("{0}{1}", current, secondPart);
return UInt64.Parse(nextValueFormatted).ToString("X");
}
}
22 chars, url safe, and retains Guid uniqueness.
// Our url safe, base 64 alphabet:
const string alphabet = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Sanitized Guid string. Preserve the last two hex chars
var guidStr = "929F7C4D4B2644E1A122A379C02D6345";
var lastTwo = guidStr.Substring(30, 2);
string shortGuid = "";
// Iterate over the ten groups of 3 hex chars: 929 F7C 4D4 B26 44E 1A1 22A 379 C02 D63
for (var i = 0; i < 10; i++)
{
var hex = guidStr.Substring(i*3, 3); // Get the next 3 hex chars
var x = Convert.ToInt32(hex, 16); // Convert to int
shortGuid += $"{alphabet[x/64]}{alphabet[x%64]}"; // Lookup the two-digit base64 value
}
shortGuid += lastTwo; // Don't forget the last two
Console.WriteLine(shortGuid);
Output:
yDXWhiGAfc4v6EbTK0Px45
Based on #dorcohen's answer and #pootzko's comment.
You can use this. It is safe over the wire.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
In C# a long value has 64 bits, which if encoded with Base64, there will be 12 characters, including 1 padding =. If we trim the padding =, there will be 11 characters.
One crazy idea here is we could use a combination of Unix Epoch and a counter for one epoch value to form a long value. The Unix Epoch in C# DateTimeOffset.ToUnixEpochMilliseconds is in long format, but the first 2 bytes of the 8 bytes are always 0, because otherwise the date time value will be greater than the maximum date time value. So that gives us 2 bytes to place an ushort counter in.
So, in total, as long as the number of ID generation does not exceed 65536 per millisecond, we can have an unique ID:
// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;
var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
// Use big endian
epochBytes = epochBytes.Reverse().ToArray();
}
// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
// Use big endian
counterBytes = counterBytes.Reverse().ToArray();
}
// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);
// Encode the byte array and trim padding '='
// e.g. AAsBcTCCVlg
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');
If you dont need to type the string you could use the following:
static class GuidConverter
{
public static string GuidToString(Guid g)
{
var bytes = g.ToByteArray();
var sb = new StringBuilder();
for (var j = 0; j < bytes.Length; j++)
{
var c = BitConverter.ToChar(bytes, j);
sb.Append(c);
j++;
}
return sb.ToString();
}
public static Guid StringToGuid(string s)
=> new Guid(s.SelectMany(BitConverter.GetBytes).ToArray());
}
This will convert the Guid to a 8 character String like this:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} --> "䦥띺ᠫ䋺ꦃ亂檽趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} --> "엸詼䔑架펊䑫ᝦ"
to not lose characters (+ / -) and if you want to use your guid in an url, it must be transformed into base32
for 10 000 000 no duplicate key
public static List<string> guids = new List<string>();
static void Main(string[] args)
{
for (int i = 0; i < 10000000; i++)
{
var guid = Guid.NewGuid();
string encoded = BytesToBase32(guid.ToByteArray());
guids.Add(encoded);
Console.Write(".");
}
var result = guids.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key);
foreach (var res in result)
Console.WriteLine($"Duplicate {res}");
Console.WriteLine($"*********** end **************");
Console.ReadLine();
}
public static string BytesToBase32(byte[] bytes)
{
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
string output = "";
for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5)
{
int dualbyte = bytes[bitIndex / 8] << 8;
if (bitIndex / 8 + 1 < bytes.Length)
dualbyte |= bytes[bitIndex / 8 + 1];
dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
output += alphabet[dualbyte];
}
return output;
}
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{
lock(_getUniqueIdLock)
{
System.Threading.Thread.Sleep(1);
return DateTime.UtcNow.Ticks.ToString("X");
}
}
you can use
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
its 6 nice characters only, 599527 ,143354
and when user virify it simply
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
hope this help you
Guid.NewGuid().ToString().Split('-').First()
I have a character array as shown below :
char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFG" +
"HIJKLMNOPQRSTUVWXYZ0123456789`~!##$%^&*()-_=+[]{}\\|;:'\",<" +
".>/?".ToCharArray();
and from this char array, i want to generate a string of minimum length 7 and all the characters in the string should be from the above char array.
How to do this operation?
What's the maximum length? (You may want to parameterize the methods below to specify a minimum and maximum length.) Here's a simple way of doing it for exactly 7 characters:
Here's the C# version:
public string GeneratePassword(Random rng)
{
char[] chars = new char[7];
for (int i = 0; i < chars.Length; i++)
{
chars[i] = pwdCharArray[rng.Next(pwdCharArray.Length)];
}
return new string(chars);
}
Note that the Random instance should be passed in to avoid the common problem of creating many instances of Random. I have an article describing this problem and ways around it. In essence, you should use one instance of Random per thread - don't create a new instance every time you want to use one, and don't reuse the same instance across multiple threads.
In fact, for a genuine password which is guarding sensitive information, you probably shouldn't be using Random at all, but rather something like RNGCryptoServiceProvider (or less directly, the results of RandomNumberGenerator.Create()). This can be somewhat harder to use, but will give you much more secure random numbers.
In Java it would be pretty similar, but then I'd use SecureRandom (which is fortunately rather easier to use than its .NET counterpart). In this case, you can create a new instance each time:
public String generatePassword() {
char[] chars = new char[7];
SecureRandom rng = new SecureRandom();
for (int i = 0; i < chars.length; i++) {
chars[i] = pwdCharArray[nextInt(pwdCharArray.length)];
}
return new String(chars);
}
Generate 7 random numbers between 0 and yourArray.length -1.
Pick the corresponding char in the array and put it in your final String.
Here is the code with a StringBuilder:
StringBuilder sb = new StringBuilder();
Random random = new Randdom();
for(int i=0; i<7; i++) {
sb.append(pwdCharArray[random.nextInt(0, pwdCharArray.length -1)]);
}
return sb.toString();
(C# example)
Random rand = new Random();
char[] arr = new char[rand.Next(7,15)];
for(int i = 0 ; i < arr.Length ; i++) {
arr[i] = pwdCharArray[rand.Next(pwdCharArray.Length)];
}
string pwd = new string(arr);
Here is a solution in Java, with variable length of the word, and test of working:
import java.util.*;
public class RandomWord {
// Valid characters
private static String VALID_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFG" +
"HIJKLMNOPQRSTUVWXYZ0123456789`~!##$%^&*()-_=+[]{}\\|;:'\",<.>/?";
// Minimal length thw word can have
private static int MIN_LENGTH = 7;
// Maximal length will be MIN_LENGTH + THRESHOLD - 1
private static int THRESHOLD = 10;
// Generate a random number generator.
// The time in millis is used as seed prevents the numbers to be always the same in same order
Random randomGenerator = new Random(System.currentTimeMillis());
public String generateWord () {
// Actual length of the word (from MIN_LENGTH to MIN_LENGTH + THRESHOLD - 1)
int length = MIN_LENGTH + randomGenerator.nextInt(THRESHOLD);
// Loop for every character
StringBuilder word = new StringBuilder();
for (int i = 0; i < length; i++) {
// Appends one more random char
word.append(VALID_CHARACTERS.charAt(randomGenerator.nextInt(VALID_CHARACTERS.length())));
}
// Returns the random word
return word.toString();
}
// Test the class
public static void main (String[] args) {
// Instantiates and tests the class
RandomWord randomWord = new RandomWord();
for (int i = 0; i < 30; i++) {
String word = randomWord.generateWord();
System.out.println(word + " (" + word.length() + ")");
}
}
}