I'm trying to create something that will be like a basic random password generator including uppercase, lowercase and digits - but for some reason this
static void Main(string[] args)
{
bool validLength = false;
int userDefinedLength = 0;
Console.WriteLine("How many characters would you like your password to be?");
do
{
try
{
userDefinedLength = int.Parse(Console.ReadLine());
validLength = true;
if (userDefinedLength < 3)
{
Console.WriteLine("Please enter something larger than 3.");
validLength = false;
}
}
catch (Exception)
{
Console.WriteLine("Please input a valid integer length.");
}
} while (validLength == false);
char[] passwordArray = new char[userDefinedLength];
int asciiValue = 0;
char asciiChar = ' ';
bool validPassword = false;
Random ranAsciiGroup = new Random();
Random ascValue = new Random();
do
{
for (int i = 0; i < passwordArray.Length; i++)
{
int randomAsc = 0;
randomAsc = ranAsciiGroup.Next(1, 4);
//Console.WriteLine(randomAsc);
if (randomAsc == 1)
{
asciiValue = ascValue.Next(65, 91);
//Console.WriteLine(asciiValue);
}
else if (randomAsc == 2)
{
asciiValue = ascValue.Next(97, 123);
//Console.WriteLine(asciiValue);
}
else if (randomAsc == 3)
{
asciiValue = ascValue.Next(48, 58);
//Console.WriteLine(asciiValue);
}
asciiChar = (char)asciiValue;
passwordArray[i] = asciiChar;
//validPassword = true;
}
bool isDigit = false;
bool isUpper = false;
bool isLower = false;
for (int i = 0; i < passwordArray.Length; i++)
{
if (char.IsDigit(passwordArray[i]))
{
isDigit = true;
}
if (char.IsUpper(passwordArray[i]))
{
isUpper = true;
}
if (char.IsLower(passwordArray[i]))
{
isLower = true;
}
}
if (isDigit == true && isUpper == true && isLower == true)
{
validPassword = true;
}
} while (validPassword == false);
Console.WriteLine("Your password is...");
Console.ForegroundColor = ConsoleColor.DarkGreen;
foreach (char c in passwordArray)
{
Console.Write(c);
}
Console.ReadLine();
}
The password that it produces seems to not be using any numbers that are less than 6. And some of the characters that it produces are quite repeated - e.g. the lower case tend to have characters that appear much more than some others - or some that don't appear at all. I'll leave a 100 character example here.
m9nj88m8GBpF7Hk87E8p9CAE987pEj7pm7j89iHplo7DIpB87B9irlAk9Ik9C8q8i97B9o8l8GDjj88j898Dmk9A69969Ino988I
Seed your RNG
Don't forget to seed your instance of random, e.g.
var random = new System.Random(Environment.TickCount);
Also, one instance should be enough.
Eliminating repetition
If you wish to ensure that all characters are represented, you can use a different random selection technique. For example, you could generate a very very long string that contains all the characters you want, sort it randomly, then take the first n characters. This approach would completely eliminate repeated characters and guarantee that every character gets used eventually. For example:
using System;
using System.Linq;
public class Program
{
static readonly Random _random = new System.Random(Environment.TickCount);
const string dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklpmnopqrstuvwxyz0123456789!##$%^&*()_+-=";
static string GetPassword(int length)
{
return new String
(
dictionary
.OrderBy( a => _random.NextDouble() )
.Take(length)
.ToArray()
);
}
public static void Main()
{
var s = GetPassword(30);
Console.WriteLine(s);
}
}
In this example we treat the string as an Enumerable<char> (yes, this is allowed) and use LINQ methods to sort it randomly and take the first n characters. We then pass the results to the constructor for a new System.String containing the answer.
Sample output:
Run #1:
!#t(hfTz0rB5cvKy1d$oeVI2mnsCba
Run #2:
y7Y(MB1pWH$wO5XPD0b+%Rkn9a4F!_
Run #3:
tH92lnh*sL+WOaTYR160&xiZpC5#G3
Looks pretty random to me.
Controlled repetition
The above solution of course only allows at most one instance of each character in the dictionary. But maybe you want them to be able to appear more than once, but not too much. If you'd like a controlled, limited number of possible repeats, you can make a small change:
static string GetPassword(int length, int maxRepeats)
{
return new String
(
Enumerable.Range(0, maxRepeats)
.SelectMany( i => dictionary )
.OrderBy( a => _random.NextDouble() )
.Take(length)
.ToArray()
);
}
In this example we clone the dictionary maxRepeats times and concatenate them using SelectMany. Then we sort that gigantic string randomly and take the first n characters for the password.
Working code on .NET Fiddle
The System.Random class is not designed to be a strong RNG, it is designed to be a fast RNG. If you want a quality random number generator you need to use one based on the System.Security.Cryptography.RandomNumberGenerator class.
Here is a gist I found that uses a crypto random generator as the internals of the existing random class.
Running it with the new generator gives me
mm77D5EDjO0OhOOe8kppiY0toc0HWQjpo37b4LFj56LvcQvA4jE83J8BS8xeX6zcEr2Od8A70v2xFKiY0ROY3gN105rZt6PE8F2i
which you can see appears to not have the biases you found.
Related
I have a school homework, where I need to count how many times each letter is in a txt file, I can't use a dictionary and can't use LinQ, I then need to put it in order alphabetically, and in order of iterations.
string = "hello world"
output =
D=1
E=1
H=1
L=3
O=2
R=1
W=1
L=3
O=2
D=1
E=1
H=1
R=1
W=1
What I have so far works just for the order alphabetically , not for the itterations.
public void testChar() {
string text = File.ReadAllText(# "C:\Ecole\Session 2\Prog\Bloc 4\test.txt")?.ToUpper();
text = Regex.Replace(text, # "[^a-zA-Z]", "");
List < char > listChar = new List < char > ();
foreach(char lettre in text) {
listChar.Add(lettre);
}
int countPosition = 0;
List < int > position = new List < int > ();
listOfChar.Add(listChar[0]);
listOfRepetitions.Add(1);
position.Add(countPosition);
int jumpFirstItteration = 0;
foreach(var item in listChar) {
if (jumpFirstItteration == 0) {
jumpFirstItteration++;
}
if (listOfChar.Contains(item)) {
int pos = listOfChar.IndexOf(item);
listOfRepetitions[pos] += 1;
} else if (!listOfChar.Contains(item)) {
listOfChar.Add(item);
listOfRepetitions.Add(1);
countPosition++;
position.Add(countPosition);
}
}
}
Please help :D
The canonical way of computing a concordance is to use an array of integers for counting the letters, the same size as the number of different letters in the text - in this case, just the normal uppercase alphabetic characters A-Z.
Then you iterate through the uppercased letters and if they are in range, increment the count corresponding to that letter.
To simplify this you can make two observations:
To convert a letter to an index, just subtract 'A' from the character code.
To convert an index to a letter, just add 'A' to the index and cast the result back to a char. (The cast is necessary because the result of adding an int to a char is an int, not a char.)
Once you've done that, you'll have all the counts for the characters in alphabetical order. However, you also need the letters in order of frequency of occurrence. To compute that, you can use an overload of Array.Sort() that takes two arrays: The first parameter is an array to sort, and the second parameter is an array to sort in the same way as the first array.
If you pass the array of counts as the first array, and an array of all the letters being counted in alphabetical order as the second array (i.e. the letters A..Z) then after sorting the second array will give you the letters in the correct order to display with the first array.
Putting all that together:
public void testChar()
{
string filename = #"C:\Ecole\Session 2\Prog\Bloc 4\test.txt";
string text = File.ReadAllText(filename).ToUpper();
int[] concordance = new int[26]; // 26 different letters of the alphabet to count.
foreach (char c in text)
{
int index = c - 'A'; // A..Z will convert to 0..25; other chars will be outside that range.
if (index >= 0 && index < 26)
++concordance[index];
}
// Display frequency in alphabetic order, omitting chars with 0 occurances.
for (int i = 0; i < concordance.Length; ++i)
{
if (concordance[i] > 0)
Console.WriteLine($"{(char)('A'+i)} = {concordance[i]}");
}
Console.WriteLine();
// For sorting by frequency we need another array of chars A..Z in alphabetical order.
char[] aToZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
Array.Sort(concordance, aToZ);
// Display frequency in occurance order, omitting chars with 0 occurances.
for (int i = 0; i < concordance.Length; ++i)
{
if (concordance[i] > 0)
Console.WriteLine($"{aToZ[i]} = {concordance[i]}");
}
}
maybe add something like a sort function?
position.sort((a,b)=> if a < b return 1; if a > b return -1; return 0;)
This would be a normal sorting function based on their integer value, but since you are dealing with a list, it becomes a little more tedious.
position.sort((a,b)=> if a[i] < b[i] return 1; if a[i] > b[i] return -1; return 0;)
where i the index of integers to be compared
Forgive me, I come from java
Not necessarilly the most efficient, but works.
First, a simple letter class thant can compare its properies.
public class Letter
{
public char Symbole { get; set; }
public int Frequency { get; set; }
public int CompareLetter(object obj)
{
Letter other = obj as Letter;
if (other == null) return 1;
return this.Symbole.CompareTo(other.Symbole);
}
public int CompareFrequency(object obj)
{
Letter other = obj as Letter;
if (other == null) return 1;
return this.Frequency.CompareTo(other.Frequency);
}
}
Then a method to populate a List of Letter
public static List<Letter> ReturnLettersCount(string fileName)
{
string text;
List<Letter> letters = new List<Letter>();
using (StreamReader sr = new StreamReader(fileName))
{
text = sr.ReadToEnd().ToLower();
}
foreach (char c in text)
{
if (char.IsLetter(c))
{
Letter letter = letters.Find((letter) => letter.Symbole == c);
if (letter == null)
{
letters.Add(new Letter() { Symbole = c, Frequency = 1 });
}
else
{
letter.Frequency++;
}
}
}
return letters;
}
And a user code
static void Main(string[] args)
{
string fileName = #"path\to\your\textFile.txt";
List<Letter> letters = ReturnLettersCount(fileName);
letters.Sort( (a, b) => a.CompareLetter(b) );
foreach(Letter letter in letters)
{
Console.WriteLine($"{letter.Symbole}: {letter.Frequency}");
}
Console.WriteLine("--------------------------");
letters.Sort((b, a) => a.CompareFrequency(b));
foreach (Letter letter in letters)
{
Console.WriteLine($"{letter.Symbole}: {letter.Frequency}");
}
}
Without Linq and Dictionary it's like walking by foot to the grocery 20 miles away. ;)
//C# vs >= 8.0
using System;
using Entry = System.Collections.Generic.KeyValuePair<char, int>;
// class header ...
public static void Sort() {
var text = "hello world";
var contest = new System.Collections.Generic.List<Entry>(text.Length);
foreach (var c in text) {
if (!char.IsLetter(c)) {
continue;
}
var i = contest.FindIndex(kv => kv.Key == c);
if (i < 0) {
contest.Add(new(c, 1));
}
else {
contest[i] = new(c, contest[i].Value + 1);
}
}
contest.Sort((e1, e2) => e1.Key - e2.Key);
Console.Write("\n{0}\n\n", string.Join('\n', contest));
contest.Sort((e1, e2) => e2.Value - e1.Value);
Console.Write("\n{0}\n\n", string.Join('\n', contest));
}
A few suggestions that may come in handy.
You could build a container (the CharInfo class object here), to store the information you collect about each char found, it's position and how many times it's found in your text file.
This class container implements IComparable (the comparer just tests the char value, as an integer, against the char value of another object).
This makes it simple to sort a List<CharInfo>, just calling its Sort() method:
public class CharInfo : IComparable<CharInfo>
{
public CharInfo(char letter, int position) {
Character = letter;
Positions.Add(position);
}
public char Character { get; }
public int Occurrences { get; set; } = 1;
public List<int> Positions { get; } = new List<int>();
public static List<string> CharsFound { get; } = new List<string>();
public int CompareTo(CharInfo other) => this.Character - other.Character;
}
Since you can use the Regex class, you can then use Regex.Matches() to return all characters in the [a-zA-Z] range. Each Match object also stores the position where the character was found.
(As a note, you could just use the Matches collection and GroupBy() the results, but you cannot use LINQ and the other suggestions would just be mute :)
Looping the Matches collection, you test whether a char is already present in the list; if it is, add 1 to the Occurrences Property and add its position to the Positions Property.
If it's not already there, add a new CharInfo object to the collection. The constructor of the class takes a char and a position. The Occurrences Property defaults to 1 (you create a new CharInfo because you have found a new char, which is occurrence n° 1).
In the end, just Sort() the collection using its custom comparer. Sort() will call the IComparable.CompareTo() method of the class.
string text = File.ReadAllText([File Path]);
var matches = Regex.Matches(text, #"[a-zA-Z]", RegexOptions.Multiline);
var charsInfo = new List<CharInfo>();
foreach (Match m in matches) {
int pos = CharInfo.CharsFound.IndexOf(m.Value);
if (pos >= 0) {
charsInfo[pos].Occurrences += 1;
charsInfo[pos].Positions.Add(m.Index);
}
else {
CharInfo.CharsFound.Add(m.Value);
charsInfo.Add(new CharInfo(m.Value[0], m.Index));
}
}
// Sort the List<CharInfo> using the provided comparer
charsInfo.Sort();
Call CharInfo.CharsFound.Clear() before you search another text file.
You can then print the results as:
foreach (var chInfo in charsInfo) {
Console.WriteLine(
$"Char: {chInfo.Character} " +
$"Occurrences: {chInfo.Occurrences} " +
$"Positions: {string.Join(",", chInfo.Positions)}");
}
Note that upper and lower case chars are treated as distinct elements.
Modify as required.
I am making a tic tac toe game and I am making it to be unbeatable
but first I have to let the computer know the rules.
I'm stuck in a step that is => when it's the computer's turn and we have just started the game so no win case so it's up to the computer to generate a random number that will be the computer's choice ( the block where he mark X or O )
so I need it to generate a number from 1 to 9 but by excluding the already used blocks ( numbers ).
I tried doing that by making a list and adding a number every time the human player used a block but I can't find a way to use those numbers from the list as exclusion for the random choice of the computer.
Here is what I tried and thnx in advance:
//random
List<int> cas = new List<int>();
if (c1 == true)
{
cas.Add(1);
}
if (c2 == true)
{
cas.Add(2);
}
if (c3 == true)
{
cas.Add(3);
}
if (c4 == true)
{
cas.Add(4);
}
if (c5 == true)
{
cas.Add(5);
}
if (c6 == true)
{
cas.Add(6);
}
if (c7 == true)
{
cas.Add(7);
}
if (c8 == true)
{
cas.Add(8);
}
if (c9 == true)
{
cas.Add(9);
}
for (int i = 0; i < cas.Count; i++)
{
random_except_list(cas[]);
}
public static int random_except_list(int[] x)
{
Random r = new Random();
int result = r.Next(1, 9 - );
for (int i = 0; i < x.Length; i++)
{
if (result < x[i])
return result;
result++;
}
return result;
}
Lets have possible places to use:
List<int> possible = Enumerable.Range(1,9).ToList(); // create a list and add 1-9
and used places:
List<int> used = new List<int>();
Random rnd = new Random();
Now everytime we generate a random number in the range of possible list count as index and remove it from there and move it to used:
int index = rnd.Next(0, possible.Count);
used.Add(possible[index]);
possible.RemoveAt(index);
for user its just enough to check if it exists in the used so the acceptable number should be:
!used.Any(x=> x== NumberUserHaveChosen)
So the first time the random number can be 0-8 (as possible.Count==9) and take from it at random index.
the second time the random number can be 0-7 (as possible.Count==8) and take from it at random index.
and so on... while the possible.Count != 0
in this case there is no need to generate random numbers several times that finally it won't exist in our used List.
Several years ago I was working on a Sudoku algorithm, and what I was trying to achieve was to generate a valid solved sudoku table in minimum time possible, i get to the conclusion that I should replace the algorithm that each time I generate a number I have to check some lists to make sure the number was not generated before, as count of numbers were increasing these comparisons would become more and more. for example when only the number 4 is remaining, I should generate random numbers till I get 4. so I used this approach and the result was amazing.
I think you should do something like this:
public static int random_except_list(List<int> x)
{
Random r = new Random();
int result = 0;
while (true)
{
result = r.Next(1, 10);
if (!x.Contains(result))
return result;
}
}
Just trying to use what you have written (more or less), but changing the method to take a List<int> which is what you are building, I would write the method using LINQ like this (except I would create a static Random variable and keep it between calls):
public static int random_except_list(List<int> x) => Enumerable.Range(1, 9).Where(n => !x.Contains(n)).ToList()[new Random().Next(0, 9 - x.Count)];
However, you can implement the same idea in a more explicit way using procedural code:
public static int random_except_list_explicit(List<int> x) {
// First, generate a list of possible answers by skipping the except positions in x
var possibles = new List<int>();
for (int i = 1; i <= 9; i++)
if (!x.Contains(i))
possibles.Add(i);
// now pick a random member of the possible answers and return it
return possibles[new Random().Next(0, possibles.Count)];
}
I believe it is better to work with positions left. So user or computer select from all open positions:
using System;
using System.Collections.Generic;
class app
{
public static int random_except_list(List<int> openPositions)
{
Random r = new Random();
int index = r.Next(openPositions.Count - 1);
int result = openPositions[index];
openPositions.RemoveAt(index);
return result;
}
static void Main()
{
List<int> openPositions = new List<int>();
for (int i = 1; i < 10; i++)
{
openPositions.Add(i);
}
bool turn = false;
while (openPositions.Count > 0)
{
foreach (int value in openPositions)
{
Console.Write(value + " ");
}
Console.WriteLine();
if (!turn)
{
while (true)
{
Console.WriteLine("Choose your Position");
ConsoleKeyInfo key = Console.ReadKey();
int num = (int)key.KeyChar - 48;
if (openPositions.Contains(num))
{
openPositions.Remove(num);
Console.WriteLine();
break;
}
else Console.Write(" is not Valid: ");
}
}
else
{
int compPos = random_except_list(openPositions);
Console.WriteLine("Computer choose: " + compPos);
}
turn = !turn;
}
Console.WriteLine("No positions left");
Console.ReadKey();
}
}
According to following code , I need to generate a string in different cases based on the method input. My issue is where I wanna generate 9A9A (at least 1 number and 1 letter) or 9A9A9A (at least 2 numbers and 2 letters). In most cases, this conditions is not met.
private AuthMessage GetAuthCode(string CodeType) //(out string Message)
{
Guid Guid = Guid.NewGuid();
Random Random = new Random();
string AuthCode = string.Empty;
string RefCode = string.Empty;
RefCode = Guid.ToString("N");
switch (CodeType)
{
case "0": //9999
{
AuthCode = Random.Next(1000, 9999).ToString();
break;
}
case "1": //99999
{
AuthCode = Random.Next(10000, 99999).ToString();
break;
}
case "2": //999999
{
AuthCode = Random.Next(100000, 999999).ToString();
break;
}
case "3": //999-999
{
AuthCode = Regex.Replace(Random.Next(100000, 999999).ToString(), #"^(.{3})(.{3})$", "$1-$2");
break;
}
case "4": //9A9A
{
AuthCode = Guid.ToString("N").Substring(14, 4).ToUpper();
break;
}
case "5": //9A9A9
{
AuthCode = Guid.ToString("N").Substring(15, 5).ToUpper();
break;
}
case "6": //9A9A9A
{
AuthCode = Guid.ToString("N").Substring(6, 6).ToUpper();
break;
}
case "7": //9A9-A9A
{
AuthCode = Regex.Replace(Guid.ToString("N").Substring(6, 6), #"(.{3})(.{3})", #"$1-$2").ToUpper();
break;
}
case "8": //9A9-A9A
{
AuthCode = Regex.Replace(Regex.Replace(Convert.ToBase64String(Guid.ToByteArray()), "[/+=]", "").Substring(0, 6), #"(.{3})(.{3})", #"$1-$2").ToUpper();
break;
}
default:
{
AuthCode = Random.Next(1000, 9999).ToString();
break;
}
}
AuthMessage Response = new AuthMessage();
Response.AuthCode = AuthCode;
Response.RefCode = RefCode;
return Response;
}
Guid representation is composed of hexadecimal digits, i.e. characters 0-9 and a-f. The problem with relying on it to obtain a mixture of letters and numbers is that a character at any given position could be either a letter or a decimal digit, with probability tilted roughly 5:3 in favor of a decimal digit.
If you want to generate a specific mix of digits and letters, you should generate the string one character at a time, without relying on Guid representation.
I thought I'd have a go - it gives me a good chance of being ridiculed. This isn't the most efficient way of generating the codes, but it should be fairly random.
private string GetAuthCode(string CodeType)
{
var patterns = new Dictionary<char, Func<Char>>()
{
{ '9', () => RandomBytes().Where(x => x >= '0' && x <= '9').First() },
{ 'A', () => RandomBytes().Where(x => x >= 'A' && x <= 'Z').First() },
{ '-', () => '-' },
};
return
String.IsNullOrEmpty(CodeType)
? ""
: patterns[CodeType[0]]().ToString() + GetAuthCode(CodeType.Substring(1));
}
private IEnumerable<char> RandomBytes()
{
using (var rng = System.Security.Cryptography.RNGCryptoServiceProvider.Create())
{
var bytes = new byte[256];
while (true)
{
rng.GetBytes(bytes);
foreach (var #byte in bytes)
{
yield return (char)#byte;
}
}
}
}
Now, due to the funky monkey state machine that implements iterator methods, this code does dispose of the RNG despite the while (true).
I simplified the GetAuthCode method slightly, but I think this demonstrates a suitable way to generate the codes.
public class TokenCreator
{
private Random random = new Random();
private const string[] chars= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
public static string CreateToken(int length)
{
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
}
AuthCode = TokenCreator.CreateToken(5);
This implementation can little help you. But again it doesn't guarantee you get alphanumeric combination. Sometime you may get only alphabets and sometime you may get only numbers. Though chances are so little.
OverView The following code uses Regex.Replace along woth IsMatch which allows one to create an any length pattern such as XXXXXX or XX-XX which will replace any X with a random character or letter.
For example XX-XX could return A(-1d or with dashes just XXXX to A(1d.
Security
It uses the Web.Security.Membership.GeneratePassword to get a string of 48 random characters, digits and what not. Then a clear password is culled from that random characters based on the pattern needed.
(at least 1 number and 1 letter)
This is done in the validation regex which assures there is atleast one alphabetic character and one number. That generate method is called until that validation reports a valid match from the rules you mentioned.
Secure Transport
Finally a secure string is setup to be returned.
This one works off of getting a
// This pattern enforces our rule that a pwd must have one letter and one digit.
string pattern = #" # This regex pattern enforces the rules before returning matching
(?=.*[a - zA - Z]) # Somewhere there is a an alphabectic character
(?=.*\d) # Somewhere there is a number; if no number found return no match.
(.+) # Successful match, rules are satisfied. Return match";
Random rn = new Random(); // Used to cherry pick from chars to use.
// Creates 48 alpha and non alpha (at least 10 non digit alphas) random characters.
string charsToUse = System.Web.Security.Membership.GeneratePassword(48, 5);
// When replacement is done, replace an `X` matched with a random char.
MatchEvaluator RandomChar = delegate (Match m)
{
return charsToUse[rn.Next(charsToUse.Length)].ToString();
};
Func<string, string> Validate =
(string str) => Regex.IsMatch(str, pattern, RegexOptions.IgnorePatternWhitespace)
? str : string.Empty; // return empty on failure.
string pwdClear = string.Empty;
// Generate valid pwd based on rules. Loop until rules are met.
while (string.IsNullOrEmpty(pwdClear))
pwdClear = Validate(Regex.Replace("XXXX-XXXX-XXXX-XXXX-XXXX", "X", RandomChar));
// Create a secure string for the password for transportation.
SecureString ss = new SecureString();
pwdClear.ToList()
.ForEach(chr => ss.AppendChar(chr));
This answer is based off of a non-secure implementation on my blog see C#: Generate a Random Sequence of Numbers and Letters From a User Defined Pattern and Characters
For testing purposes, when I need to generate random strings with specific properties, I use something similar to this. You might have to adapt it to your needs.
public sealed class StringGenerator
{
private static readonly char[] NumericChars = "0123456789".ToCharArray();
private static readonly char[] LowerAlphaChars = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
private static readonly char[] UpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
public StringGenerator(IRandom rnd)
{
Rnd = rnd ?? new SecureRandom();
}
private IRandom Rnd
{
get;
set;
}
public string Generate(int length, int minNumeric = 0, int minAlpha = 0, AlphaCase alphaCase = AlphaCase.Both)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException("length");
}
if (minNumeric < 0)
{
throw new ArgumentOutOfRangeException("minNumeric");
}
if (minAlpha < 0)
{
throw new ArgumentOutOfRangeException("minAlpha");
}
if (length < minNumeric + minAlpha)
{
throw new ArgumentException();
}
if (length == 0)
{
return string.Empty;
}
var result = new char[length];
var index = 0;
foreach(var numeric in GenerateNumeric().Take(minNumeric))
{
result[index++] = numeric;
}
var alphaCharacters = GetAlphaCharacters(alphaCase);
foreach (var alpha in Generate(alphaCharacters).Take(minAlpha))
{
result[index++] = alpha;
}
var restLength = length - index;
if (restLength > 0)
{
var restCharacters = new List<char>(NumericChars.Concat(alphaCharacters));
foreach (var rest in Generate(restCharacters).Take(restLength))
{
result[index++] = rest;
}
}
// shuffle result
return new string(result.OrderBy(x => Rnd.Next()).ToArray());
}
private IList<char> GetAlphaCharacters(AlphaCase alphaCase)
{
switch (alphaCase)
{
case AlphaCase.Lower:
return LowerAlphaChars;
case AlphaCase.Upper:
return UpperAlphaChars;
case AlphaCase.Both:
default:
return new List<char>(LowerAlphaChars.Concat(UpperAlphaChars));
}
}
public IEnumerable<char> GenerateNumeric()
{
return Generate(NumericChars);
}
public IEnumerable<char> GenerateLowerAlpha()
{
return Generate(LowerAlphaChars);
}
public IEnumerable<char> GenerateUpperAlpha()
{
return Generate(UpperAlphaChars);
}
public IEnumerable<char> Generate(IList<char> characters)
{
if (characters == null)
{
throw new ArgumentNullException();
}
if (!characters.Any())
{
yield break;
}
while (true)
{
yield return characters[Rnd.Next(characters.Count)];
}
}
}
public enum AlphaCase
{
Lower,
Upper,
Both
}
public interface IRandom
{
int Next();
int Next(int maxValue);
}
public sealed class SecureRandom : IRandom
{
private readonly RandomNumberGenerator Rng = new RNGCryptoServiceProvider();
public int Next()
{
var data = new byte[sizeof(int)];
Rng.GetBytes(data);
return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
}
public int Next(int maxValue)
{
return Next(0, maxValue);
}
public int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException();
}
return (int)Math.Floor(minValue + ((double)maxValue - minValue) * NextDouble());
}
public double NextDouble()
{
var data = new byte[sizeof(uint)];
Rng.GetBytes(data);
var randomUint = BitConverter.ToUInt32(data, 0);
return randomUint / (uint.MaxValue + 1d);
}
}
Edit: This is an answer to the question of how to generate random alphanumeric strings with conditions. As stated for testing purposes. Not to be used as is in a context where security is relevant.
Edit 2: Shamelessly borrowing from this answer, the solution now uses a wrapper around RNGCryptoServiceProvider. Still not sure if this should be used in security relevant context, but at least now it should be "better" than simply using Random
I'm trying to do a simple string generation based on pattern.
My idea was to use Regex to do simple replace.
I've started with simple method:
private static string parseTemplate(string template)
{
return Regex.Replace(template, #"(\[d)((:)?([\d]+)?)\]", RandomDigit());
}
private static string RandomDigit()
{
Random r = new Random();
return r.Next(0, 9).ToString();
}
What this does for now is replacing groups like [d], [d:3] with what supposed to be random digit.
Unfortunately every group is replaced with the same digit, for example if I put test [d][d][d:3] my method will return test 222.
I would like to get different digit in every place, like test 361.
Second problem I have is way to handle length:
right now I must specify [d] for every digit I want, but it would be easier to specify [d:3] and get the same output.
I know that there is a project called Fare, but I would like to do this without this library
For now I only search for [d], but is this method will work fine there won't be a problem to add other groups for example: [s] for special characters or any other type of patters.
Edit1
As it was suggested I changed Random to a static variable like so:
private static string parseTemplate(string template)
{
return Regex.Replace(template, #"(\[d)((:)?([\d]+)?)\]", RandomDigit());
}
private static Random r = new Random();
private static string RandomDigit()
{
return r.Next(0, 9).ToString();
}
Problem is that when I call my code like so:
Console.WriteLine(parseTemplate("test [d:2][d:][d]"));
Console.WriteLine(parseTemplate("test [d:2][d:][d]"));
I get output like this
test 222
test 555
I would like output like this (for example):
test 265
test 962
I think that problem is with Regex.Replace which calls my RandomDigit only once.
For your first issue: When you call new Random() you are seeding with the same value every time you call the function - initialise a static Random member variable once then return r.Next(0,9).ToString();
Edit:
In answer to your comment, try using MatchEvaluator with a delegate, something like the following (untested):
static string RandomReplacer(Match m)
{
var result = new StringBuilder();
foreach (char c in m.ToString())
result.Append(c == 'd' ? RandomDigit() : c);
return result.ToString()
}
private static string parseTemplate(string template)
{
return Regex.Replace(template, #"(\[d)((:)?([\d]+)?)\]", new MatchEvaluator(RandomReplacer));
}
You can then extend this approach to match [d:3] and parse it in your MatchEvaluator accordingly, solving your second issue.
Assumnig [d:3] means "three random digits", the following MatchEvaluator uses the length (read from group 4) to generate a random digit string:
static string ReplaceSingleMatch(Match m)
{
int length;
if (!int.TryParse(m.Groups[1].Value, out length))
length = 1;
char[] chars = new char[length];
for (int i = 0; i < chars.Length; i++)
chars[i] = RandomDigit()[0];
return new string(chars);
}
You can then call this as follows:
private static string parseTemplate(string template)
{
return Regex.Replace(template, #"\[d(?::(\d+))?\]", ReplaceSingleMatch);
}
You might want to then change RandomDigit to return a single char rather than a string, or to take an int and return multiple characters.
private static string GenerateMask(string mask)
{
StringBuilder output = new StringBuilder();
for (int i = 0; i < mask.Length; i++)
{
if (mask[i] == 'd' && mask[i - 1] != '\\')
{
int quantifier = 1;
if (mask[i + 1] == ':')
Int32.TryParse(mask[i + 2].ToString(), out quantifier);
output.Append(GetRandomDigit(quantifier));
i += 2;
}
else
{
if(mask[i] != '\\')
output.Append(mask[i]);
}
}
return output.ToString();
}
private static string GetRandomDigit(int length)
{
Random random = new Random();
StringBuilder output = new StringBuilder();
while (output.Length != length)
output.Append(random.Next(0, 9));
return output.ToString();
}
There's a custom algorithm I just put together for fun mostly and here's the implementation:
Console.WriteLine(GenerateMask(#"Hey Da\d, meet my new girlfrien\d she's d:2"));
//Output: Hey Dad, meet my new girlfriend she's 44
There is a library to generate Random numbers, so why isn't there a library for generation of random strings?
In other words how to generate a random string, and specify desired length, or better, generate unique string on specification you want i.e. specify the length, a unique string within my application is enough for me.
I know I can create a Guid (Globally Unique IDentifier) but those are quite long, longer they need to be.
int length = 8;
string s = RandomString.NextRandomString(length)
uniquestringCollection = new UniquestringsCollection(length)
string s2 = uniquestringCollection.GetNext();
I can't recall where I got this, so if you know who originally authored this, please help me give attribution.
private static void Main()
{
const string AllowedChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz##$^*()";
Random rng = new Random();
foreach (string randomString in rng.NextStrings(AllowedChars, (15, 64), 25))
{
Console.WriteLine(randomString);
}
Console.ReadLine();
}
private static IEnumerable<string> NextStrings(
this Random rnd,
string allowedChars,
(int Min, int Max)length,
int count)
{
ISet<string> usedRandomStrings = new HashSet<string>();
(int min, int max) = length;
char[] chars = new char[max];
int setLength = allowedChars.Length;
while (count-- > 0)
{
int stringLength = rnd.Next(min, max + 1);
for (int i = 0; i < stringLength; ++i)
{
chars[i] = allowedChars[rnd.Next(setLength)];
}
string randomString = new string(chars, 0, stringLength);
if (usedRandomStrings.Add(randomString))
{
yield return randomString;
}
else
{
count++;
}
}
}
How about-
static Random rd = new Random();
internal static string CreateString(int stringLength)
{
const string allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789!#$?_-";
char[] chars = new char[stringLength];
for (int i = 0; i < stringLength; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
How about
string.Join("", Enumerable.Repeat(0, 100).Select(n => (char)new Random().Next(127)));
or
var rand = new Random();
string.Join("", Enumerable.Repeat(0, 100).Select(n => (char)rand.Next(127)));
Random string using Concatenated GUIDs
This approach uses the minimum number of concatenated GUIDs to return a random string of the desired number of characters.
/// <summary>
/// Uses concatenated then SubStringed GUIDs to get a random string of the
/// desired length. Relies on the randomness of the GUID generation algorithm.
/// </summary>
/// <param name="stringLength">Length of string to return</param>
/// <returns>Random string of <paramref name="stringLength"/> characters</returns>
internal static string GetRandomString(int stringLength)
{
StringBuilder sb = new StringBuilder();
int numGuidsToConcat = (((stringLength - 1) / 32) + 1);
for(int i = 1; i <= numGuidsToConcat; i++)
{
sb.Append(Guid.NewGuid().ToString("N"));
}
return sb.ToString(0, stringLength);
}
Example output with a length of 8:
39877037
2f1461d8
152ece65
79778fc6
76f426d8
73a27a0d
8efd1210
4bc5b0d2
7b1aa10e
3a7a5b3a
77676839
abffa3c9
37fdbeb1
45736489
Example output with a length of 40 (note the repeated '4' - in a v4 GUID, thee is one hex digit that will always be a 4 (effectively removing 4 bits) -- the algorithm could be improved by removing the '4' to improve randomness, given that the returned length of the string can be less than the length of a GUID (32 chars)...):
e5af105b73924c3590e99d2820e3ae7a3086d0e3
e03542e1b0a44138a49965b1ee434e3efe8d063d
c182cecb5f5b4b85a255a397de1c8615a6d6eef5
676548dc532a4c96acbe01292f260a52abdc4703
43d6735ef36841cd9085e56f496ece7c87c8beb9
f537d7702b22418d8ee6476dcd5f4ff3b3547f11
93749400bd494bfab187ac0a662baaa2771ce39d
335ce3c0f742434a904bd4bcad53fc3c8783a9f9
f2dd06d176634c5b9d7083962e68d3277cb2a060
4c89143715d34742b5f1b7047e8107fd28781b39
2f060d86f7244ae8b3b419a6e659a84135ec2bf8
54d5477a78194600af55c376c2b0c8f55ded2ab6
746acb308acf46ca88303dfbf38c831da39dc66e
bdc98417074047a79636e567e4de60aa19e89710
a114d8883d58451da03dfff75796f73711821b02
C# Fiddler Demo: https://dotnetfiddle.net/Y1j6Dw
I commonly use code like the following to generate a random string:
internal static class Utilities {
static Random randomGenerator = new Random();
internal static string GenerateRandomString(int length) {
byte[] randomBytes = new byte[randomGenerator.Next(length)];
randomGenerator.NextBytes(randomBytes);
return Convert.ToBase64String(randomBytes);
}
}
This will create a Base64 encoded string of the random bytes generated by the random object. It isn't thread safe so multiple threads will have to lock around it. Additionally I use a static Random object so two calls to the method at the same time won't get the same initial seed.
A library for generating random strings wouldn't be very useful. It would either be too simple, so that you often need to replace it anyway, or too complex in order to be able to cover any possible situation, that you replace it because it's to complicated to use.
Creating random strings is quite easy by using the random geneator for numbers, given the exact details of your needs. It's just more efficient to write the code specifically for each situation.
If you want a unique string, there is two possibilities. You can either keep every random string that is created, so that you can check for uniqueness, or you can make it really long so that it's incredibly unlikely that there would be duplicates. A GUID does the latter (which explains why it's so long), so there is already an implementation for that.
Sometimes a random string can be useful in something like a unit test, if it was me I would add the AutoFixture nuget package and then do something like this.
In my example I need a 121 character string...
var fixture = new Fixture();
var chars = fixture.CreateMany<char>(121).ToArray();
var myString = new string(chars);
If this wasn't in a unit test then I'd create myself a nuget package and use Autofixture in that, or something else if I needed only certain character types.
You've sort of answered your own question; there is no RandomString() function because you can use a random number generator to generate a string easily.
1) Make the range of numbers between the ASCII characters that you wish to include
2) Append each character to a string until the desired length is reached.
3) Rise and repeat for each string needed (add them to an array, etc.)
Surely that'd do it?
How about something like this...
static string GetRandomString(int lenOfTheNewStr)
{
string output = string.Empty;
while (true)
{
output = output + Path.GetRandomFileName().Replace(".", string.Empty);
if (output.Length > lenOfTheNewStr)
{
output = output.Substring(0, lenOfTheNewStr);
break;
}
}
return output;
}
Output
static void Main(string[] args)
{
for (int x = 0; x < 10; x++)
{
string output = GetRandomString(20);
Console.WriteLine(output);
}
Console.ReadKey();
}
r323afsluywgozfpvne4
qpfumdh3pmskleiavi3x
nq40h0uki2o0ptljxtpr
n4o0rzwcz5pdvfhmiwey
sihfvt1pvofgxfs3etxg
z3iagj5nqm4j1f5iwemg
m2kbffbyqrjs1ad15kcn
cckd1wvebdzcce0fpnru
n3tvq0qphfkunek0220d
ufh2noeccbwyfrtkwi02
Online Demo: https://dotnetfiddle.net/PVgf0k
References
• https://www.dotnetperls.com/random-string
public static String getAlphaNumericString(int n) {
String AlphaNumericString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "abcdefghijklmnopqrstuvxyz";
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) {
// generate a random number between
// 0 to AlphaNumericString variable length
int index = (int) (AlphaNumericString.length() * Math.random());
// add Character one by one in end of sb
sb.append(AlphaNumericString.charAt(index));
}
return sb.toString();
}
Here is some modification from above answers.
private static string GetFixedLengthStrinng(int len)
{
const string possibleAllChars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789!##$%^&*()_+{}:',.?/";
StringBuilder sb = new StringBuilder();
Random randomNumber = new Random();
for (int i = 0; i < len; i++)
{
sb.Append(possibleAllChars[randomNumber.Next(0, possibleAllChars.Length)]);
}
return sb.ToString();
}