Problem
In C#, how can I make a random 8-character alphanumeric string?
Asked by KingNestor
Solution #1
I’ve heard LINQ is the new black, so here’s my LINQ attempt:
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: Because of the usage of the Random class, this is unsuitable for any security-related tasks, such as producing passwords or tokens.) If you need a strong random number generator, use the RNGCryptoServiceProvider class.)
Answered by dtb
Solution #2
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);
The solution isn’t as elegant as Linq’s.
(Note: Because of the usage of the Random class, this is unsuitable for any security-related tasks, such as producing passwords or tokens.) If you need a strong random number generator, use the RNGCryptoServiceProvider class.)
Answered by Dan Rigby
Solution #3
This one, unlike some of the alternatives, 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 possibilities here, and updated/modified in response to the comments.
Here’s a quick test harness that shows the character distribution in the original and modified output. Random.org has a comprehensive overview of randomness analysis.
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")}%");
}
}
}
}
Answered by Eric J.
Solution #4
Solution 1: The largest ‘range’ with the most length flexibility
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);
}
}
Because a GUID comprises a few of fixed bits that are always the same and thus not random, this technique has wider range than using a GUID. For example, the 13 character in hex is always “4” – at least in a version 6 GUID.
You may also construct a string of arbitrary length using this solution.
Solution 2 – A single line of code that can accommodate up to 22 characters
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
Although you won’t be able to generate strings as long as Solution 1 and the string won’t have the same range due to fixed bits in GUIDs, this will suffice in many applications.
Solution 3: Slightly less lines of code
Guid.NewGuid().ToString("n").Substring(0, 8);
This is mostly being kept for historical purposes. It uses somewhat less code, but at the cost of having a smaller range: because it uses hex instead of base64, it requires more characters to express the same range than the other options.
Which means there’s a higher chance of a collision – 100,000 iterations of 8 character strings yielded one duplicate.
Answered by Douglas
Solution #5
Here’s an example I took from Sam Allen’s Dot Net Perls site.
Use Path if you just need 8 characters. The System.IO namespace has a function called GetRandomFileName(). Sam claims that he is utilizing the “Because it uses RNGCryptoServiceProvider for improved randomization, the Path.GetRandomFileName method is occasionally superior. It is, however, limited to 11 characters at random.”
GetRandomFileName returns a 12-character string with a period at the ninth character. As a result, you’ll need to remove the period (which isn’t random) and then take 8 characters from the string. Actually, you could skip the period and just take the first eight characters.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS: thanks Sam
Answered by Adam Porad
Post is based on https://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings