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:
- Four character sets (UpperCase, LowerCase, Numeric, and Special characters)
- Set your own character set
- Minimal number of each type of the characters
- Pattern driven string generation
- Unique string generation
- 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.
Legend:
Green
- public methods Red
- private methodsBlue
- 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.
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 = "";
if (PatternDriven)
{
result = PatternDrivenAlgo(Pattern);
}
else if (MinUpperCaseCharacters == 0 && MinLowerCaseCharacters == 0 &&
MinNumericCharacters == 0 && MinSpecialCharacters == 0)
{
result = SimpleGenerateAlgo(length);
}
else
{
result = GenerateAlgoWithLimits(length);
}
if (UniqueStrings && ExistingStrings.Contains(result))
return GenerateString(length);
AddExistingString(result);
return result;
}
The simplest used algorithm for string generation. It's called only for all limits (e.g., MinUpperCaseCharacters
equals to 0).
private string SimpleGenerateAlgo(int length)
{
string result = "";
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.
private string GenerateAlgoWithLimits(int length)
{
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 = "";
List<char> Characters = new List<char>();
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));
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.
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:
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();
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();
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);
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);
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:
- Fixed size string generation
- Variable size string generation
- 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)
- String generation using only letters and numbers
- String generation using only numbers and special characters
- Using a custom alphabet for the generation
- Using minimal limits for string generation
- Using each character only once during the generation
- Using multiple settings
- Unique strings test
History
- Version 1.1: Some code explanations and optimizations were made. Thanks to the comments.
- Version 1.0: Initial release.