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
Related
Im trying to write a function complex_decode( string str) in c sharp that takes a non-simple repeated encoded string, and returns the original un-encoded string.
for example, "t11h12e14" would return "ttttttttttthhhhhhhhhhhheeeeeeeeeeeeee". I have been successful in decoding strings where the length is less than 10, but unable to work with length for than 10. I am not allowed to use regex, libraries or loops. Only recursions.
This is my code for simple decode which decodes when length less than 10.
public string decode(string str)
{
if (str.Length < 1)
return "";
if(str.Length==2)
return repeat_char(str[0], char_to_int(str[1]));
else
return repeat_char(str[0], char_to_int(str[1]))+decode(str.Substring(2));
}
public int char_to_int(char c)
{
return (int)(c-48);
}
public string repeat_char(char c, int n)
{
if (n < 1)
return "";
if (n == 1)
return ""+c;
else
return c + repeat_char(c, n - 1);
}
This works as intended, for example input "a5" returns "aaaaa", "t1h1e1" returns "the"
Any help is appreciated.
Here is another way of doing this, assuming the repeating string is always one character long and using only recursion (and a StringBuilder object):
private static string decode(string value)
{
var position = 0;
var result = decode_char(value, ref position);
return result;
}
private static string decode_char(string value, ref int position)
{
var next = value[position++];
var countBuilder = new StringBuilder();
get_number(value, ref position, countBuilder);
var result = new string(next, Convert.ToInt32(countBuilder.ToString()));
if (position < value.Length)
result += decode_char(value, ref position);
return result;
}
private static void get_number(string value, ref int position, StringBuilder countBuilder)
{
if (position < value.Length && char.IsNumber(value[position]))
{
countBuilder.Append(value[position++]);
get_number(value, ref position, countBuilder);
}
}
I've refactored your code a bit. I've removed 2 unnecessary methods that you don't actually need. So, the logic is simple and it works like this;
Example input: t3h2e4
Get the first digit. (Which is 2 and has index of 1)
Get the first letter comes after that index, which is our next letter. (Which is "h" and has index of 2)
Slice the string. Start from index 1 and end the slicing on index 2 to get repeat count. (Which is 3)
Repeat the first letter of string for repeat count times and combine it with the result you got from step 5.
Slice the starting from the next letter index we got in second step, to the very end of the string and pass this to recursive method.
public static string Decode(string input)
{
// If the string is empty or has only 1 character, return the string itself to not proceed.
if (input.Length <= 1)
{
return input;
}
// Convert string into character list.
var characters = new List<char>();
characters.AddRange(input);
var firstDigitIndex = characters.FindIndex(c => char.IsDigit(c)); // Get first digit
var nextLetterIndex = characters.FindIndex(firstDigitIndex, c => char.IsLetter(c)); // Get the next letter after that digit
if (nextLetterIndex == -1)
{
// This has only one reason. Let's say you are in the last recursion and you have c2
// There is no letter after the digit, so the index will -1, which means "not found"
// So, it will raise an exception, since we try to use the -1 in slicing part
// Instead, if it's not found, we set the next letter index to length of the string
// With doing that, you either get repeatCount correctly (since remaining part is only digits)
// or you will get empty string in the next recursion, which will stop the recursion.
nextLetterIndex = input.Length;
}
// Let's say first digit's index is 1 and the next letter's index is 2
// str[2..3] will start to slice the string from index 2 and will stop in index 3
// So, it will basically return us the repeat count.
var repeatCount = int.Parse(input[firstDigitIndex..nextLetterIndex]);
// string(character, repeatCount) constructor will repeat the "character" you passed to it for "repeatCount" times
return new string(input[0], repeatCount) + Decode(input[nextLetterIndex..]);
}
Examples;
Console.WriteLine(Decode("t1h1e1")); // the
Console.WriteLine(Decode("t2h3e4")); // tthhheeee
Console.WriteLine(Decode("t3h3e3")); // ttthhheee
Console.WriteLine(Decode("t2h10e2")); // tthhhhhhhhhhee
Console.WriteLine(Decode("t2h10e10")); // tthhhhhhhhhheeeeeeeeee
First you can simplify your repeat_char function, you have to have a clear stop condition:
public static string repeat_char(char c, int resultLength)
{
if(resultLength < 1) throw new ArgumentOutOfRangeException("resultLength");
if(resultLength == 1) return c.ToString();
return c + repeat_char(c, resultLength - 1);
}
See the use of the parameter as equivalent of a counter on a loop.
So you can have something similar on the main function, a parameter that tells when your substring is not an int anymore.
public static string decode(string str, int repeatNumberLength = 1)
{
if(repeatNumberLength < 1) throw new ArgumentOutOfRangeException("length");
//stop condition
if(string.IsNullOrWhiteSpace(str)) return str;
if(repeatNumberLength >= str.Length) repeatNumberLength = str.Length; //Some validation, just to be safe
//keep going until str[1...repeatNumberLength] is not an int
int charLength;
if(repeatNumberLength < str.Length && int.TryParse(str.Substring(1, repeatNumberLength), out charLength))
{
return decode(str, repeatNumberLength + 1);
}
repeatNumberLength--;
//Get the repeated Char.
charLength = int.Parse(str.Substring(1, repeatNumberLength));
var repeatedChar = repeat_char(str[0], charLength);
//decode the substring
var decodedSubstring = decode(str.Substring(repeatNumberLength + 1));
return repeatedChar + decodedSubstring;
}
I used a default parameter, but you can easily change it for a more traditonal style.
This also assumes that the original str is in a correct format.
An excellent exercise is to change the function so that you can have a word, instead of a char before the number. Then you could, for example, have "the3" as the parameter (resulting in "thethethe").
I took more of a Lisp-style head and tail approach (car and cdr if you speak Lisp) and created a State class to carry around the current state of the parsing.
First the State class:
internal class State
{
public State()
{
LastLetter = string.Empty;
CurrentCount = 0;
HasStarted = false;
CurrentValue = string.Empty;
}
public string LastLetter { get; private set; }
public int CurrentCount { get; private set; }
public bool HasStarted { get; private set; }
public string CurrentValue { get; private set; }
public override string ToString()
{
return $"LastLetter: {LastLetter}, CurrentCount: {CurrentCount}, HasStarted: {HasStarted}, CurrentValue: {CurrentValue}";
}
public void AddLetter(string letter)
{
CurrentCount = 0;
LastLetter = letter;
HasStarted = true;
}
public int AddDigit(string digit)
{
if (!HasStarted)
{
throw new InvalidOperationException($"The input must start with a letter, not a digit");
}
if (!int.TryParse(digit, out var num))
{
throw new InvalidOperationException($"Digit passed to {nameof(AddDigit)} ({digit}) is not a number");
}
CurrentCount = CurrentCount * 10 + num;
return CurrentCount;
}
public string GetValue()
{
if (string.IsNullOrEmpty(LastLetter))
{
return string.Empty;
}
CurrentValue = new string(LastLetter[0], CurrentCount);
return CurrentValue;
}
}
You'll notice it's got some stuff in there for debugging (example, the ToString override and the CurrentValue property)
Once you have that, the decoder is easy, it just recurses over the string it's given (along with (initially) a freshly constructed State instance):
private string Decode(string input, State state)
{
if (input.Length == 0)
{
_buffer.Append(state.GetValue());
return _buffer.ToString();
}
var head = input[0];
var tail = input.Substring(1);
var headString = head.ToString();
if (char.IsDigit(head))
{
state.AddDigit(headString);
}
else // it's a character
{
_buffer.Append(state.GetValue());
state.AddLetter(headString);
}
Decode(tail, state);
return _buffer.ToString();
}
I did this in a simple Windows Forms app, with a text box for input, a label for output and a button to crank her up:
const string NotAllowedPattern = #"[^a-zA-Z0-9]";
private static Regex NotAllowedRegex = new Regex(NotAllowedPattern);
private StringBuilder _buffer = new StringBuilder();
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Length == 0 || NotAllowedRegex.IsMatch(textBox1.Text))
{
MessageBox.Show(this, "Only Letters and Digits Allowed", "Bad Input", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
label1.Text = string.Empty;
_buffer.Clear();
var result = Decode(textBox1.Text, new State());
label1.Text = result;
}
Yeah, there's a Regex there, but it's just to make sure that the input is valid; it's not involved in calculating the output.
I Have a string with special chars and i have to replace those chars with an index (padded n '0' left).
Fast example for better explanation:
I have the string "0980 0099 8383 $$$$" and an index (integer) 3
result should be "0980 0099 8383 0003"
The special characters are not necessarily in sequence.
the source string could be empty or it may not contain any special characters
I've already written functions that works.
public static class StringExtensions
{
public static string ReplaceCounter(this string source, int counter, string character)
{
string res = source;
try
{
if (!string.IsNullOrEmpty(character))
{
if (res.Contains(character))
{
// Get ALL Indexes position of character
var Indexes = GetIndexes(res, character);
int max = GetMaxValue(Indexes.Count);
while (counter >= max)
{
counter -= max;
}
var new_value = counter.ToString().PadLeft(Indexes.Count, '0');
for (int i = 0; i < Indexes.Count; i++)
{
res = res.Remove(Indexes[i], 1).Insert(Indexes[i], new_value[i].ToString());
}
}
}
}
catch (Exception)
{
res = source;
}
return res;
}
private static List<int> GetIndexes(string mainString, string toFind)
{
var Indexes = new List<int>();
for (int i = mainString.IndexOf(toFind); i > -1; i = mainString.IndexOf(toFind, i + 1))
{
// for loop end when i=-1 (line.counter not found)
Indexes.Add(i);
}
return Indexes;
}
private static int GetMaxValue(int numIndexes)
{
int max = 0;
for (int i = 0; i < numIndexes; i++)
{
if (i == 0)
max = 9;
else
max = max * 10 + 9;
}
return max;
}
}
but i don't really like it (first of all because i'm passing the char as string.. and not as a char).
string source = "000081059671####=1811";
int index = 5;
string character = "#";
string result = source.ReplaceCounter(index, character);
can it be more optimized and compact?
Can some good soul help me?
Thanks in advance
EDIT
The index is variable so:
If the index is 15
string source = "000081059671####=1811";
int index = 15;
string character = "#";
string result = source.ReplaceCounter(index, character);
// result = "0000810596710015=1811"
it should be a check if the index > max number
in my code i posted above, if this case happened i remove from index the "max" value until index < max number
What is mux number? if the special chars number is 4 (as in the example below) the max number will be 9999
string source = "000081059671####=1811";
// max number 9999
Yet another edit
From a comment it seems that more than one digit can be used. In this case the counter can be converted to a string and treated as a char[] to pick the character to use in each iteration :
public static string ReplaceCounter(this string source,
int counter,
char character)
{
var sb=new StringBuilder(source);
var replacements=counter.ToString();
int r=replacements.Length-1;
for(int i=sb.Length-1;i>=0;i--)
{
if(sb[i]==character)
{
sb[i]=r>=0 ? replacements[r--] : '0';
}
}
return sb.ToString();
}
This can be used for any number of digits."0980 0099 8383 $$$$".ReplaceCounter(15,'$') produces 0980 0099 8383 0015
An edit
After posting the original answer I remembered one can modify a string without allocations by using a StringBuilder. In this case, the last match needs to be replaced with one character, all other matches with another. This ca be a simple reverse iteration :
public static string ReplaceCounter(this string source,
int counter,
char character)
{
var sb=new StringBuilder(source);
bool useChar=true;
for(int i=sb.Length-1;i>=0;i--)
{
if(sb[i]==character)
{
sb[i]=useChar?(char)('0'+counter):'0';
useChar=false;
}
}
return sb.ToString();
}
Console.WriteLine("0000##81#059671####=1811".ReplaceCounter(5,'#'));
Console.WriteLine("0980 0099 8383 $$$$".ReplaceCounter(3,'$'));
------
0000008100596710005=1811
0980 0099 8383 0003
Original Answer
Any string modification operation produces a new temporary string that need to be garbage collected. This adds up so quickly that avoiding temporary strings can result in >10x speed improvements when processing lots of text or lots of requests. That's better than using parallel processing.
You can use Regex.Replace to perform complex replacements without allocating temporary strings. You can use one of the Replace overloads that use a MatchEvaluator to produce dynamic output, not just a single value.
In this case :
var source = "0000##81#059671####=1811";
var result = Regex.Replace(source,"#", m=>m.NextMatch().Success?"0":"5");
Console.WriteLine(result);
--------
0000008100596710005=1811
Match.NextMatch() returns the next match in the source, so m.NextMatch().Success can be used to identify the last match and replace it with the index.
This would fail if the character was one of the Regex pattern characters. This can be avoided by escaping the character with Regex.Escape(string)
This can be packed in an extension method
public static string ReplaceCounter(this string source,
int counter,
string character)
{
return Regex.Replace(source,
Regex.Escape(character),
m=>m.NextMatch().Success?"0":counter.ToString());
}
public static string ReplaceCounter(this string source,
int counter,
char character)
=>ReplaceCounter(source,counter,character.ToString());
This code
var source= "0980 0099 8383 $$$$";
var result=source.ReplaceCounter(5,"$");
Returns
0980 0099 8383 0003
I would suggest such solutiuon (got rid out of helper methods:
public static class StringExtensions
{
public static string ReplaceCounter(this string source, int counter, char character)
{
string res = source;
string strCounter = counter.ToString();
bool counterTooLong = false;
int idx;
// Going from the and backwards, we fill with counter digits.
for(int i = strCounter.Length - 1; i >= 0; i--)
{
idx = res.LastIndexOf(character);
// if we run out of special characters, break the loop.
if (idx == -1)
{
counterTooLong = true;
break;
}
res = res.Remove(idx, 1).Insert(idx, strCounter[i].ToString());
}
// If we could not fit the counter, we simply throw exception
if (counterTooLong) throw new InvalidOperationException();
// If we did not fill all placeholders, we fill it with zeros.
while (-1 != (idx = res.IndexOf(character))) res = res.Remove(idx, 1).Insert(idx, "0");
return res;
}
}
Here's fiddle
I want to be able to generate random strings based on custom pattern set from the user using Console.ReadLine();
Example: !!!!-##!?-?!!# ( ! = number, # - Upper letter and ? = Lower letter), then generates a string and returns it using Console.WriteLine();
I tried to search for solutions but I don't think it matches my problem.
We make functions for generating uppercase and lowercase letters, we also do a quick generation of numbers and then we just switch on the characters of the passed "pattern" string
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetStringFromPattern("!!##??##!!"));
}
static string GetStringFromPattern(string s)
{
string final = String.Empty;
foreach(char c in s)
{
switch (c)
{
case '!':
Random rng = new Random();
final += rng.Next(0, 9);
break;
case '#':
final += GenerateLetter(true);
break;
case '?':
final += GenerateLetter(false);
break;
}
}
return final;
}
static char GenerateLetter(bool uppercase)
{
Random rng = new Random();
int letterCode = rng.Next(65, 89);
if (uppercase)
letterCode += 32;
char letter = (char)letterCode;
return letter;
}
}
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.
I have a c# class like so
internal class QueuedMinimumNumberFinder : ConcurrentQueue<int>
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new ConcurrentQueue<int>(number.ToCharArray().Where(m => (int) Char.GetNumericValue(m) != 0).Select(m=>(int)Char.GetNumericValue(m)).OrderBy(m=>m));
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
int next;
if (queueIndex == 0)
{
numQueue.TryDequeue(out next);
Enqueue(next);
} else
{
if (zeroes > 0)
{
Enqueue(0);
zeroes--;
} else
{
numQueue.TryDequeue(out next);
Enqueue(next);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (Count > 0)
{
int next = 0;
TryDequeue(out next);
builder.Append(next.ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
The point of the program is to find the minimum possible integer that can be made by taking out any x amount of characters from a string(example 100023 is string, if you take out any 3 letters, the minimum int created would be 100). My question is, is this the correct way to do this? Is there a better data structure that can be used for this problem?
First Edit:
Here's how it looks now
internal class QueuedMinimumNumberFinder
{
private readonly string _minString;
public QueuedMinimumNumberFinder(string number, int takeOutAmount)
{
var queue = new Queue<int>();
if (number.Length < takeOutAmount)
{
throw new Exception("Error *");
}
var queueIndex = 0;
var queueAmount = number.Length - takeOutAmount;
var numQueue = new List<int>(number.Where(m=>(int)Char.GetNumericValue(m)!=0).Select(m=>(int)Char.GetNumericValue(m))).ToList();
var zeroes = number.Length - numQueue.Count;
while (queueIndex < queueAmount)
{
if (queueIndex == 0)
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
} else
{
if (zeroes > 1)
{
queue.Enqueue(0);
zeroes--;
} else
{
var nextMin = numQueue.Min();
numQueue.Remove(nextMin);
queue.Enqueue(nextMin);
}
}
queueIndex++;
}
var builder = new StringBuilder();
while (queue.Count > 0)
{
builder.Append(queue.Dequeue().ToString());
}
_minString = builder.ToString();
}
public override string ToString() { return _minString; }
}
A pretty simple and efficient implementation can be made, once you realize that your input string digits map to the domain of only 10 possible values: '0' .. '9'.
This can be encoded as the number of occurrences of a specific digit in your input string using a simple array of 10 integers: var digit_count = new int[10];
#MasterGillBates describes this idea in his answer.
You can then regard this array as your priority queue from which you can dequeue the characters you need by iteratively removing the lowest available character (decreasing its occurrence count in the array).
The code sample below provides an example implementation for this idea.
public static class MinNumberSolver
{
public static string GetMinString(string number, int takeOutAmount)
{
// "Add" the string by simply counting digit occurrance frequency.
var digit_count = new int[10];
foreach (var c in number)
if (char.IsDigit(c))
digit_count[c - '0']++;
// Now remove them one by one in lowest to highest order.
// For the first character we skip any potential leading 0s
var selected = new char[takeOutAmount];
var start_index = 1;
selected[0] = TakeLowest(digit_count, ref start_index);
// For the rest we start in digit order at '0' first.
start_index = 0;
for (var i = 0; i < takeOutAmount - 1; i++)
selected[1 + i] = TakeLowest(digit_count, ref start_index);
// And return the result.
return new string(selected);
}
private static char TakeLowest(int[] digit_count, ref int start_index)
{
for (var i = start_index; i < digit_count.Length; i++)
{
if (digit_count[i] > 0)
{
start_index = ((--digit_count[i] > 0) ? i : i + 1);
return (char)('0' + i);
}
}
throw new InvalidDataException("Input string does not have sufficient digits");
}
}
Just keep a count of how many times each digit appears. An array of size 10 will do. Count[i] gives the count of digit i.
Then pick the smallest non-zero i first, then pick the smallest etc and form your number.
Here's my solution using LINQ:
public string MinimumNumberFinder(string number, int takeOutAmount)
{
var ordered = number.OrderBy(n => n);
var nonZero = ordered.SkipWhile(n => n == '0');
var zero = ordered.TakeWhile(n => n == '0');
var result = nonZero.Take(1)
.Concat(zero)
.Concat(nonZero.Skip(1))
.Take(number.Length - takeOutAmount);
return new string(result.ToArray());
}
You could place every integer into a list and find all possible sequences of these values. From the list of sequences, you could sort through taking only the sets which have the number of integers you want. From there, you can write a quick function which parses a sequence into an integer. Next, you could store all of your parsed sequences into an array or an other data structure and sort based on value, which will allow you to select the minimum number from the data structure. There may be simpler ways to do this, but this will definitely work and gives you options as far as how many digits you want your number to have.
If I'm understanding this correctly, why don't you just pick out your numbers starting with the smallest number greater than zero. Then pick out all zeroes, then any remaining number if all the zeroes are picked up. This is all depending on the length of your ending result
In your example you have a 6 digit number and you want to pick out 3 digits. This means you'll only have 3 digits left. If it was a 10 digit number, then you would end up with a 7 digit number, etc...
So have an algorithm that knows the length of your starting number, how many digits you plan on removing, and the length of your ending number. Then just pick out the numbers.
This is just quick and dirty code:
string startingNumber = "9999903040404"; // "100023";
int numberOfCharactersToRemove = 3;
string endingNumber = string.Empty;
int endingNumberLength = startingNumber.Length - numberOfCharactersToRemove;
while (endingNumber.Length < endingNumberLength)
{
if (string.IsNullOrEmpty(endingNumber))
{
// Find the smallest digit in the starting number
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
else if (startingNumber.Contains("0"))
{
// Add any zeroes
endingNumber += "0";
startingNumber = startingNumber.Remove(startingNumber.IndexOf("0"), 1);
}
else
{
// Add any remaining numbers from least to greatest
for (int i = 1; i <= 9; i++)
{
if (startingNumber.Contains(i.ToString()))
{
endingNumber += i.ToString();
startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
break;
}
}
}
}
Console.WriteLine(endingNumber);
100023 starting number resulted in 100 being the end result
9999903040404 starting number resulted in 3000044499 being the end result
Here's my version to fix this problem:
DESIGN:
You can sort your list using a binary tree , there are a lot of
implementations , I picked this one
Then you can keep track of the number of the Zeros you have in your
string Finally you will end up with two lists: I named one
SortedDigitsList and the other one ZeroDigitsList
perform a switch case to determine which last 3 digits should be
returned
Here's the complete code:
class MainProgram2
{
static void Main()
{
Tree theTree = new Tree();
Console.WriteLine("Please Enter the string you want to process:");
string input = Console.ReadLine();
foreach (char c in input)
{
// Check if it's a digit or not
if (c >= '0' && c <= '9')
{
theTree.Insert((int)Char.GetNumericValue(c));
}
}
//End of for each (char c in input)
Console.WriteLine("Inorder traversal resulting Tree Sort without the zeros");
theTree.Inorder(theTree.ReturnRoot());
Console.WriteLine(" ");
//Format the output depending on how many zeros you have
Console.WriteLine("The final 3 digits are");
switch (theTree.ZeroDigitsList.Count)
{
case 0:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], theTree.SortedDigitsList[1], theTree.SortedDigitsList[2]);
break;
}
case 1:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, theTree.SortedDigitsList[2]);
break;
}
default:
{
Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, 0);
break;
}
}
Console.ReadLine();
}
}//End of main()
}
class Node
{
public int item;
public Node leftChild;
public Node rightChild;
public void displayNode()
{
Console.Write("[");
Console.Write(item);
Console.Write("]");
}
}
class Tree
{
public List<int> SortedDigitsList { get; set; }
public List<int> ZeroDigitsList { get; set; }
public Node root;
public Tree()
{
root = null;
SortedDigitsList = new List<int>();
ZeroDigitsList = new List<int>();
}
public Node ReturnRoot()
{
return root;
}
public void Insert(int id)
{
Node newNode = new Node();
newNode.item = id;
if (root == null)
root = newNode;
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (id < current.item)
{
current = current.leftChild;
if (current == null)
{
parent.leftChild = newNode;
return;
}
}
else
{
current = current.rightChild;
if (current == null)
{
parent.rightChild = newNode;
return;
}
}
}
}
}
//public void Preorder(Node Root)
//{
// if (Root != null)
// {
// Console.Write(Root.item + " ");
// Preorder(Root.leftChild);
// Preorder(Root.rightChild);
// }
//}
public void Inorder(Node Root)
{
if (Root != null)
{
Inorder(Root.leftChild);
if (Root.item > 0)
{
SortedDigitsList.Add(Root.item);
Console.Write(Root.item + " ");
}
else
{
ZeroDigitsList.Add(Root.item);
}
Inorder(Root.rightChild);
}
}