Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / tools

C# Random String Generator

4.90/5 (43 votes)
18 Apr 2013CPOL3 min read 209.9K   8.3K  
A C# class that can generate random strings and supports customisation.

Introduction

Using this class you can generate random strings like passwords and identificators, but the main feature is that you can customize it to fit your needs. To do that you don't need to modify the code, it's enough to set some settings and you'll change the behavior of the generator.

Background

I was looking for a C# class which could generate unique random strings for identificators, but I couldn't found one that fits all my needs such as minimal number of characters of each type (upper case letters, lower case letters, numbers and special characters) or usage of each character only once. So I've decided to code a class where you can set the following:

  1. Four character sets (UpperCase, LowerCase, Numeric, and Special characters)
  2. Set your own character set
  3. Minimal number of each type of the characters
  4. Pattern driven string generation
  5. Unique string generation
  6. Using each character only once

Code

The code, in general, is pretty big and you can download it if you want, so I'll describe only the essential parts. 

Image 1

Legend:

  • Green - public methods
  • Red - private methods
  • Blue - internal data

The user has three options: Generate a string of a fixed size, string of a variable size, or pattern driven string generation. Each of them will later call the general GenerateString method that will check exceptional situations and call one of three algorithms to generate a string (just like Strategy pattern does). At the end it will check if this string is unique and if we need a unique string and the generated string is not unique, it'll then recall itself (recursively, so you can get a stack overflow exception while generating a lot of small strings with a lot of restrictions). I'm using RNGCryptoServiceProvider to get random numbers, but you can change it to normal Random if you want the generation to work faster.

This method generates a string by deciding which algorithm will be used.

C#
private string GenerateString(int length)
{
    if (length == 0)
       throw new ArgumentException("You can't generate a string of a zero length");
    if (!UseUpperCaseCharacters && !UseLowerCaseCharacters && !UseNumericCharacters && !UseSpecialCharacters)
       throw new ArgumentException("There should be at least one character set in use");
    if (!RepeatCharacters && (CurrentGeneralCharacters.Length < length))
       throw new ArgumentException("There is not enough characters to create a string without repeats");
    string result = ""; // This string will contain the result
    if (PatternDriven)
    {
       // Using the pattern to generate a string
       result = PatternDrivenAlgo(Pattern);
    }
    else if (MinUpperCaseCharacters == 0 && MinLowerCaseCharacters == 0 &&
             MinNumericCharacters == 0 && MinSpecialCharacters == 0)
    {
       // Using the simpliest algorithm in this case
       result = SimpleGenerateAlgo(length);
    }
    else
    {
       // Paying attention to limits
       result = GenerateAlgoWithLimits(length);
    }
       // Support for unique strings
       // Recursion, but possibility of the stack overflow is low for big strings (> 3 chars).
       if (UniqueStrings && ExistingStrings.Contains(result))
           return GenerateString(length);
       AddExistingString(result); // Saving history
       return result;
}

The simplest used algorithm for string generation. It's called only for all limits (e.g., MinUpperCaseCharacters equals to 0).

C#
private string SimpleGenerateAlgo(int length)
{
   string result = "";
   // No special limits
   for (int i = 0; i < length; i++)
   {
      char newChar = CurrentGeneralCharacters[GetRandomInt() % CurrentGeneralCharacters.Length];
      if (!RepeatCharacters && result.Contains(newChar))
      {
         do
         {
            newChar = CurrentGeneralCharacters[GetRandomInt() % CurrentGeneralCharacters.Length];
         } while (result.Contains(newChar));
      }
         result += newChar;
   }
   return result;
}

The algorithm of the string generation paying attention for limits.

C#
private string GenerateAlgoWithLimits(int length)
{
  // exceptional situations
  if (MinUpperCaseCharacters + MinLowerCaseCharacters +
    MinNumericCharacters + MinSpecialCharacters > length)
  {
    throw new ArgumentException("Sum of MinUpperCaseCharacters, MinLowerCaseCharacters," +
    " MinNumericCharacters and MinSpecialCharacters is greater than length");
  }
  if (!RepeatCharacters && (MinUpperCaseCharacters > CurrentUpperCaseCharacters.Length))
    throw new ArgumentException("Can't generate a string with this number of MinUpperCaseCharacters");
  if (!RepeatCharacters && (MinLowerCaseCharacters > CurrentLowerCaseCharacters.Length))
    throw new ArgumentException("Can't generate a string with this number of MinLowerCaseCharacters");
  if (!RepeatCharacters && (MinNumericCharacters > CurrentNumericCharacters.Length))
    throw new ArgumentException("Can't generate a string with this number of MinNumericCharacters");
  if (!RepeatCharacters && (MinSpecialCharacters > CurrentSpecialCharacters.Length))
    throw new ArgumentException("Can't generate a string with this number of MinSpecialCharacters");
  int AllowedNumberOfGeneralChatacters = length - MinUpperCaseCharacters - MinLowerCaseCharacters
  - MinNumericCharacters - MinSpecialCharacters;
  
  string result = "";
  // generation character set in order to support unique characters
  List<char> Characters = new List<char>();
  
  // adding chars to an array
  for (int i = 0; i < MinUpperCaseCharacters; i++)
    Characters.Add(GetRandomCharFromArray(UpperCaseCharacters,Characters));
  for (int i = 0; i < MinLowerCaseCharacters; i++)
    Characters.Add(GetRandomCharFromArray(LowerCaseCharacters, Characters));
  for (int i = 0; i < MinNumericCharacters; i++)
    Characters.Add(GetRandomCharFromArray(NumericCharacters, Characters));
  for (int i = 0; i < MinSpecialCharacters; i++)
    Characters.Add(GetRandomCharFromArray(SpecialCharacters, Characters));
  for (int i = 0; i < AllowedNumberOfGeneralChatacters; i++)
    Characters.Add(GetRandomCharFromArray(CurrentGeneralCharacters, Characters));
  
  // generating result
    for (int i = 0; i < length; i++)
    {
      int position = GetRandomInt() % Characters.Count;
      char CurrentChar = Characters[position];
      Characters.RemoveAt(position);
      result += CurrentChar;
    }
    return result;
}

CurrentGeneralCharacters is generated automatically using four existing character sets: UseUpperCaseCharacters, UseLowerCaseCharacters, UseNumericCharacters, and UseSpecialCharacters. I believe that it's faster to generate it once (at the stage of parametrisation) then to regenerate it each time it's necessary.

The last string generation algorithm generates a string based on the pattern. It simply replaces the pattern's letters by random symbols from the corresponding set. And sure it pays attention to the RepeatCharacters flag.

C#
private string PatternDrivenAlgo(string Pattern)
{
    string result = "";
    List<char> Characters = new List<char>();
    foreach (char character in Pattern.ToCharArray())
    {
        char newChar = ' ';
        switch (character)
        {
            case 'L':
            {
                newChar = GetRandomCharFromArray(CurrentUpperCaseCharacters, Characters);
                break; 
            }
            case 'l':
            {
                newChar = GetRandomCharFromArray(CurrentLowerCaseCharacters, Characters);
                break; 
            }
            case 'n':
            {
                newChar = GetRandomCharFromArray(CurrentNumericCharacters, Characters);
                break; 
            }
            case 's':
            {
                newChar = GetRandomCharFromArray(CurrentSpecialCharacters, Characters);
                break;   
            }
            case '*':
            {
                newChar = GetRandomCharFromArray(CurrentGeneralCharacters, Characters);
                break;  
            }
            default:
            {
                throw new Exception("The character '" + character + "' is not supported");
            }
        }
        Characters.Add(newChar);
        result += newChar;
    }
    return result;
}

Using the code

To start using it you need to create an instance of a class and call Generate like this:

C#
Console.WriteLine("Fixed size");
RandomStringGenerator RSG = new RandomStringGenerator();
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(25));

Console.WriteLine("Variable size");
RSG = new RandomStringGenerator();
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(15,25));

Console.WriteLine("Using pattern");
RSG = new RandomStringGenerator();
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate("LLln*ssssL"));

Console.WriteLine("Using only letters and numbers");
RSG = new RandomStringGenerator();
// Or we can use the constructor
RSG.UseSpecialCharacters = false;
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(30));

Console.WriteLine("Using only special characters and numbers");
RSG = new RandomStringGenerator();
// Or we can use the constructor
RSG.UseUpperCaseCharacters = false;
RSG.UseLowerCaseCharacters = false;
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(30));
 
Console.WriteLine("Using your own alphabet");
RSG = new RandomStringGenerator(false,true,true,false);
// Or we can use the constructor
RSG.LowerCaseCharacters = "абвгдежзиклмнопрстуфхчшщюяьъ".ToCharArray();
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(30));

Console.WriteLine("Using special limits");
RSG = new RandomStringGenerator();
RSG.MinLowerCaseCharacters = 2;
RSG.MinSpecialCharacters = 20;
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(35));

Console.WriteLine("Using each character only once");
RSG = new RandomStringGenerator();
RSG.MinLowerCaseCharacters = 25;
RSG.RepeatCharacters = false;
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate(25));

Console.WriteLine("Using custom alphabet, pattern and unique chars");
RSG = new RandomStringGenerator();
RSG.LowerCaseCharacters = "абвгдежзиклмнопрстуфхчшщюяьъ".ToCharArray();
RSG.RepeatCharacters = false;
for (int i = 0; i < 5; i++)
    Console.WriteLine(RSG.Generate("lllnlllnlllsssL"));

Console.WriteLine("Unique strings test");
RSG = new RandomStringGenerator(false,false,true,false); // numbers only
RSG.UniqueStrings = true;
for (int i = 0; i < 9; i++)
    Console.WriteLine(RSG.Generate(1));

This gives the following result:

Fixed size

dDDH6S%5we=h:9=Ohm2qZ_CPm
v{]q.sIY@1|YVc/y<Pu=oY[gn
i%Ws!(X%YOQ8uc?50uf#r(OC[
kTJQ|>m*N1CA_qP>-J=%elHp[
GZaAuwdwBR<T/$9-5,pJNDn>r

Variable size

LfUf%kcb{4A#bHOlwHtX8
kX>uBUIOcU6>>mTx
$:wo)KCs=B;c{Rfz.}MLtD
V4*zH6yjQuszcZNkrz_7
mblAFP#_=b>el<>CelyS.

Using pattern

JVh8I~.=>B
JJa8d,%?]M
BXv7?{]%]I
GFz5S&!|(U
PEu8m~(%(Z

Using only letters and numbers

jbIBUPvKNc0sOsnRSVFbK43k7sVnGi
V1UdOnJtR3GUPE9lcgG6W0xbo8ibMI
y2ecnrzPJcQ74Qcewu7YHdHS5eauwm
eBYufP01BMbSddsT7tJ9MevNSnVDE8
tU3jcbB7yuHknMWDphazYSNO9QBvXm

Using only special characters and numbers

$*&!.<>52]$(89}+}]$]]5<67_03~>
~&;>#3!(4|2<1*>>$.#&?#)8+$?>0|
@^@*@.</^},>-*!61^&]-~}?}={(5|
!:{3;>{}9*3<;3:5{3,+?19}04?@./
|586<([11|.~>8*8?8]|)<>%([)0|6

Using your own alphabet

ообкбнълея4тжд8ик3л924р7псу5дя
6н2рхьрр4ш65зс35бх8ло4015ед8аж
рь4жаоупиет76хлхдпю2кщгд0о8сьг
гусю28жрж1щиьп7к8гблф7см48лб8д
сд8гч5щхчиж4чдщьс1нь9ичгщзабул

Using special limits

!I@%){.>f,S-_3k_^*s$~/$_)*>)({)%():
$[?-iQ~:~C;{a]{?^X,^g^;G{<!R{>,W]_*
?=~F>+*d}_]#9||C[*B[U^j[T%em&/r{_o.
:=K5-;1]Hg_!}+&$/9h(k-};g:%Iq#.{>GV
XP&o<;U(%:E<}L,ehi;//-:{/F2!&;!+^>@

Using each character only once

lvpeiywofrhkajdqtxcsbunzm
enfkasgqtlxzuciryjmpdbohv
oyfjnxkqtamcvzipwlsedhurb
atubmnkehwjvolzpgxfsdicqy
swqkdatpgzcjhmiyvblxnufro

Using custom alphabet, pattern and unique chars

ъхг6тею1ушь]->G
юхч8ьта7щгш)>@H
ащо8ефл0ъпз};!K
ъут8ещш5иом^$|K
ожх5ргн9тсш?~#M

Unique strings test
4
1
3
5
0
9
6 
2
7
8

So there are some examples like:

  1. Fixed size string generation
  2. Variable size string generation
  3. String generation using a pattern (L - for upper case letter, l - for lower case letter, n - for number, s - for special character and * - for any character (from all sets)
  4. String generation using only letters and numbers
  5. String generation using only numbers and special characters
  6. Using a custom alphabet for the generation
  7. Using minimal limits for string generation
  8. Using each character only once during the generation
  9. Using multiple settings
  10. Unique strings test

History  

  • Version 1.1: Some code explanations and optimizations were made. Thanks to the comments.
  • Version 1.0: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)