How can I generate a random 8 character alphanumeric string in C#?
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.
The main goals of my code are:
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);
}
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
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());
}
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.