Regex for complex password validation - c#

I looked around on stackoverflow, etc, but haven't found a good answer for this..Does regex support writing a rule like this? If so is there any regex experts our there who can help me write this, I am a big newb to regex and in a time crunch...
I know I can do this with a manual function in c# using c# char, number methods, but I would like to use regex if I can for this..
Requirement :
At least 8 characters long
2 letters
2 digits
1 Upper case
1 Lower case
1 Symbol

You can use this regex
^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z\d]).{8,}$
--------- --------------------- --------------- -----
| | | |->match 8 or more characters
| | |->match further only if theres anything except letter or digit
| |->match further only if there is an upper-lower case letter
|
|->match further only if there are two digits anywhere
Demo

You are much better off writing a simple validation routine for a password that has all of these checks.
A regular expression doesn't seem as the best (or most extensible) solution for this particular problem.
if(password.Length < 8)
return false;
var letters = 0;
var digits = 0;
var uppers = 0;
var lowers = 0;
var symbols = 0;
foreach(var ch in password)
{
if (char.IsLetter(ch)) letters++;
if (char.IsDigit(ch)) digits++;
....
}
if(letters < 2) return false;
...

I recommend going the way Oded's answer did, but I think LINQ is a more readable and explicit validation mechanism in this case.
string p = "psW%Rd32";
return p.Length >= 8 &&
p.Count(c => char.IsDigit(c)) >= 2 &&
p.Count(c => char.IsUpper(c)) >= 1 &&
p.Count(c => char.IsLower(c)) >= 1) ...

You may try this method.
private bool ValidatePassword(string password, out string ErrorMessage)
{
var input = password;
ErrorMessage = string.Empty;
if (string.IsNullOrWhiteSpace(input))
{
throw new Exception("Password should not be empty");
}
var hasNumber = new Regex(#"[0-9]+");
var hasUpperChar = new Regex(#"[A-Z]+");
var hasMiniMaxChars = new Regex(#".{8,8}");
var hasLowerChar = new Regex(#"[a-z]+");
var hasSymbols = new Regex(#"[!##$%^&*()_+=\[{\]};:<>|./?,-]");
if (!hasLowerChar.IsMatch(input))
{
ErrorMessage = "Password should contain At least one lower case letter";
return false;
}
else if (!hasUpperChar.IsMatch(input))
{
ErrorMessage = "Password should contain At least one upper case letter";
return false;
}
else if (!hasMiniMaxChars.IsMatch(input))
{
ErrorMessage = "Password should not be less than or greater than 8 characters";
return false;
}
else if (!hasNumber.IsMatch(input))
{
ErrorMessage = "Password should contain At least one numeric value";
return false;
}
else if (!hasSymbols.IsMatch(input))
{
ErrorMessage = "Password should contain At least one special case characters";
return false;
}
else
{
return true;
}
}

Related

How can I check the format of an E-Mail address?

I am building an e-mail validation program.
How can I check if my E-Mail has 3 letters before the # and 2 letters after the "." I tried else if but it didn't work.
string rightPassword = "red#gmx.to";
string enter = Console.ReadLine();
string red = rightPassword.Substring(0, 3);
string to = rightPassword.Substring(8);
int lengthRed = red.Length;
int lengthTo = to.Length;
do
{
Console.Write("Write the E-Mail: ");
enter = Console.ReadLine();
if (rightPassword == enter)
{
Console.WriteLine("Right Password");
}
else if (lengthRed < 3 ) // I have to check if it has 3 letters before the #//
{
Console.WriteLine("You need 3 letters before the #")
}
else if (lengthTo < 2)
{
Console.WriteLine("You need 2 letter after the ".")
}
} while (enter != "enter");
Just use Regular expressions for pattern matching.
You should use this regex to achieve what you want: ^\w{3}#\w+.\w{2}$
code example:
string email = "abc#aa.co";
Regex regex = new Regex(#"^\w{3}#\w+.\w{2}$");
Match match = regex.Match(email);
if(match.Success)
Console.WriteLine("Email matches pattern");
else Console.WriteLine("Email does not matches pattern");
If you don't want to use Regular Expressions, even though it is highly encouraged, you can add a method:
public static bool HasValidUsername(string email)
{
for (int i = 0; i < 3; i++)
{
if (!email[i].IsLetter())
{
return false;
}
}
return true;
}
And use the method in your code:
else if (!HasValidUsername(enter))
{
Console.WriteLine("You need 3 letters before the #");
}
But keep in mind, that the example above will not validate numbers or symbols. You could use email[i].IsSymbol() or email[i].IsNumber() to check for that.
Note:
x#com is a valid email adress for a domain registrar. That said, most of them use a subdomain for their email. x#x.com for example. Handling all real world email cases is not as trivial as it might seem.
Using Char.IsLetter() method
public bool IsValidEmail(string email)
{
return email.Length > 2 Char.IsLetter(str[0]) && Char.IsLetter(str[1]) && Char.IsLetter(str[2]);
}
You could also use Split.
Something like
string red = rightPassword.Split('#')[0];
string to = rightPassword.Split('.').Last();
you'll also be able to check what's after the "#" if you use
string red = rightPassword.Split('#')[0];
string gmx= rightPassword.Split('#')[1];
string to = rightPassword.Split('.').Last();

Split exponential number string representation into power and exponent

I have strings which come from resources in exponential form like the following: 2⁴. I was wondering if there is a way to split this into:
var base = 2; //or even "2", this is also helpful since it can be parsed
and
var exponent = 4;
I have searched the internet and msdn Standard Numeric Format Strings also, but I was unable to find the solve for this case.
You can add mapping between digits to superscript digits, then select all digits from source (this will be the base) and all the others - the exponent
const string superscriptDigits = "⁰¹²³⁴⁵⁶⁷⁸⁹";
var digitToSuperscriptMapping = superscriptDigits.Select((c, i) => new { c, i })
.ToDictionary(item => item.c, item => item.i.ToString());
const string source = "23⁴⁴";
var baseString = new string(source.TakeWhile(char.IsDigit).ToArray());
var exponentString = string.Concat(source.SkipWhile(char.IsDigit).Select(c => digitToSuperscriptMapping[c]));
Now you can convert base and exponent to int.
Also you'll need to validate input before executing conversion code.
Or even without mapping:
var baseString = new string(source.TakeWhile(char.IsDigit).ToArray());
var exponentString = string.Concat(source.SkipWhile(char.IsDigit).Select(c => char.GetNumericValue(c).ToString()));
You can use a regular expression together with String.Normalize:
var value = "42⁴³";
var match = Regex.Match(value, #"(?<base>\d+)(?<exponent>[⁰¹²³⁴-⁹]+)");
var #base = int.Parse(match.Groups["base"].Value);
var exponent = int.Parse(match.Groups["exponent"].Value.Normalize(NormalizationForm.FormKD));
Console.WriteLine($"base: {#base}, exponent: {exponent}");
The way your exponent is formatted is called superscript in English.
You can find many question related to this if you search with that keyword.
Digits in superscript are mapped in Unicode as:
0 -> \u2070
1 -> \u00b9
2 -> \u00b2
3 -> \u00b3
4 -> \u2074
5 -> \u2075
6 -> \u2076
7 -> \u2077
8 -> \u2078
9 -> \u2079
You can search for that values in your string:
Lis<char> superscriptDigits = new List<char>(){
'\u2070', \u00b9', \u00b2', \u00b3', \u2074',
\u2075', \u2076', \u2077', \u2078', \u2079"};
//the rest of the string is the expontent. Join remaining chars.
str.SkipWhile( ch => !superscriptDigits.Contains(ch) );
You get the idea
You can use a simple regex (if your source is quite clean):
string value = "2⁴⁴";
string regex = #"(?<base>\d+)(?<exp>.*)";
var matches = Regex.Matches(value, regex);
int b;
int exp = 0;
int.TryParse(matches[0].Groups["base"].Value, out b);
foreach (char c in matches[0].Groups["exp"].Value)
{
exp = exp * 10 + expToInt(c.ToString());
}
Console.Out.WriteLine("base is : {0}, exponent is {1}", b, exp);
And expToInt (based on Unicode subscripts and superscripts):
public static int expToInt(string c)
{
switch (c)
{
case "\u2070":
return 0;
case "\u00b9":
return 1;
case "\u00b2":
return 2;
case "\u00b3":
return 3;
case "\u2074":
return 4;
case "\u2075":
return 5;
case "\u2076":
return 6;
case "\u2077":
return 7;
case "\u2078":
return 8;
case "\u2079":
return 9;
default:
throw new ArgumentOutOfRangeException();
}
}
This will output:
base is 2, exp is 44

Comparing string

Is there any method that allow us to return true if string a likes string b formality?
Exam:
"12:2".Like("*:*") = true
or
"what is your name?".Like("*is*name*?")=true
Thanks!
You can use this following function using Regular Expression
Regex.IsMatch("string", "your expression")
Instance following line should return true:
Regex.IsMatch("12:2", "/[0-9]{2,}:[0-9]{1,}/")
Note: This way you have to create exp every time for different format
You can use the following method to check whether a given string matches a DOS like pattern with wildcards (i.e. a pattern that denotes one character with '?' and zero or more characters with '*'):
public static bool IsMatch(string str, string pattern)
{
string regexString = "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
Regex regex = new Regex(regexString);
return regex.IsMatch(regexString);
}
You can call it like this:
bool match = IsMatch("what is your name?", "*is*name*?"); // Returns true
You can use the following not-optimized method. The function may does not take into account some cases, but i think it gives you a point to start from.
Another possible solution is to you Regular Expressions
public static bool Like(string pattern, string str)
{
string[] words = pattern.Split('*').Where(w => w.Trim() != string.Empty).ToArray();
List<int> indeces = new List<int>();
for (int i = 0, l = words.Length; i < l; i++)
{
int wordIndex = str.IndexOf(words[i], StringComparison.OrdinalIgnoreCase);
if (wordIndex == -1)
return false;
else
indeces.Add(wordIndex);
}
List<int> sortedIndeces = indeces.ToList();
sortedIndeces.Sort();
for (int i = 0, l = sortedIndeces.Count; i < l; i++)
{
if (sortedIndeces[i] != indeces[i]) return false;
}
return true;
}
Good Luck

C# Create Acronym from Word

Given any string, I'd like to create an intelligent acronym that represents the string. If any of you have used JIRA, they accomplish this pretty well.
For example, given the word: Phoenix it would generate PHX or given the word Privacy Event Management it would create PEM.
I've got some code that will accomplish the latter:
string.Join(string.Empty, model.Name
.Where(char.IsLetter)
.Where(char.IsUpper))
This case doesn't handle if there is only one word and its lower case either.
but it doesn't account for the first case. Any ideas? I'm using C# 4.5
For the Phoenix => PHX, I think you'll need to check the strings against a dictionary of known abbreviations. As for the multiple word/camel-case support, regex is your friend!
var text = "A Big copy DayEnergyFree good"; // abbreviation should be "ABCDEFG"
var pattern = #"((?<=^|\s)(\w{1})|([A-Z]))";
string.Join(string.Empty, Regex.Matches(text, pattern).OfType<Match>().Select(x => x.Value.ToUpper()))
Let me explain what's happening here, starting with the regex pattern, which covers a few cases for matching substrings.
// must be directly after the beginning of the string or line "^" or a whitespace character "\s"
(?<=^|\s)
// match just one letter that is part of a word
(\w{1})
// if the previous requirements are not met
|
// match any upper-case letter
([A-Z])
The Regex.Matches method returns a MatchCollection, which is basically an ICollection so to use LINQ expressions, we call OfType() to convert the MatchCollection into an IEnumerable.
Regex.Matches(text, pattern).OfType<Match>()
Then we select only the value of the match (we don't need the other regex matching meta-data) and convert it to upper-case.
Select(x => x.Value.ToUpper())
I was able to extract out the JIRA key generator and posted it here. pretty interesting, and even though its JavaScript it could easily be converted to c#.
Here is a simple function that generates an acronym. Basically it puts letters or numbers into the acronym when there is a space before of this character. If there are no spaces in the string the the string is returned back. It does not capitalize letters in the acronym, but it is easy to amend.
You can just copy it in your code and start using it.
Results are the following. Just an example:
Deloitte Private Pty Ltd - DPPL
Clearwater Investment Co Pty Ltd (AC & CC Family Trust) - CICPLACFT
ASIC - ASIC
private string Acronym(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return value;
} else
{
var builder = new StringBuilder();
foreach(char c in value)
{
if (char.IsWhiteSpace(c) || char.IsLetterOrDigit(c))
{
builder.Append(c);
}
}
string trimmedValue = builder.ToString().Trim();
builder.Clear();
if (trimmedValue.Contains(' '))
{
for(int charIndex = 0; charIndex < trimmedValue.Length; charIndex++)
{
if (charIndex == 0)
{
builder.Append(trimmedValue[0]);
} else
{
char currentChar = trimmedValue[charIndex];
char previousChar = trimmedValue[charIndex - 1];
if (char.IsLetterOrDigit(currentChar) && char.IsWhiteSpace(previousChar))
{
builder.Append(trimmedValue[charIndex]);
}
}
}
return builder.ToString();
} else
{
return trimmedValue;
}
}
}
I need a not repeating code,So I create the follow method.
If you use like this,you will get
HashSet<string> idHashSet = new HashSet<string>();
for (int i = 0; i < 100; i++)
{
var eName = "China National Petroleum";
Console.WriteLine($"count:{i+1},short name:{GetIdentifierCode(eName,ref idHashSet)}");
}
the method is this.
/// <summary>
/// 根据英文名取其简写Code,优先取首字母,然后在每个单词中依次取字母作为Code,最后若还有重复则使用默认填充符(A)填充
/// todo 当名称为中文时,使用拼音作为取Code的源
/// </summary>
/// <param name="name"></param>
/// <param name="idHashSet"></param>
/// <returns></returns>
public static string GetIdentifierCode(string name, ref HashSet<string> idHashSet)
{
var words = name;
var fillChar = 'A';
if (string.IsNullOrEmpty(words))
{
do
{
words += fillChar.ToString();
} while (idHashSet.Contains(words));
}
//if (IsChinese)
//{
// words = GetPinYin(words);
//}
//中国石油天然气集团公司(China National Petroleum)
var sourceWord = new List<string>(words.Split(' '));
var returnWord = sourceWord.Select(c => new List<char>()).ToList();
int index = 0;
do
{
var listAddWord = sourceWord[index];
var addWord = returnWord[index];
//最后若还有重复则使用默认填充符(A)填充
if (sourceWord.All(c => string.IsNullOrEmpty(c)))
{
returnWord.Last().Add(fillChar);
continue;
}
//字符取完后跳过
else if (string.IsNullOrEmpty(listAddWord))
{
if (index == sourceWord.Count - 1)
index = 0;
else
{
index++;
}
continue;
}
if (addWord == null)
addWord = new List<char>();
string addString = string.Empty;
//字符全为大写时,不拆分
if (listAddWord.All(a => char.IsUpper(a)))
{
addWord = listAddWord.ToCharArray().ToList();
returnWord[index] = addWord;
addString = listAddWord;
}
else
{
addString = listAddWord.First().ToString();
addWord.Add(listAddWord.First());
}
listAddWord = listAddWord.Replace(addString, "");
sourceWord[index] = listAddWord;
if (index == sourceWord.Count - 1)
index = 0;
else
{
index++;
}
} while (idHashSet.Contains(string.Concat(returnWord.SelectMany(c => c))));
words = string.Concat(returnWord.SelectMany(c => c));
idHashSet.Add(words);
return words;

parsing of a string containing an array

I'd like to convert string containing recursive array of strings to an array of depth one.
Example:
StringToArray("[a, b, [c, [d, e]], f, [g, h], i]") == ["a", "b", "[c, [d, e]]", "f", "[g, h]", "i"]
Seems quite simple. But, I come from functional background and I'm not that familiar with .NET Framework standard libraries, so every time (I started from scratch like 3 times) I end up just plain ugly code. My latest implementation is here. As you see, it's ugly as hell.
So, what's the C# way to do this?
#ojlovecd has a good answer, using Regular Expressions.
However, his answer is overly complicated, so here's my similar, simpler answer.
public string[] StringToArray(string input) {
var pattern = new Regex(#"
\[
(?:
\s*
(?<results>(?:
(?(open) [^\[\]]+ | [^\[\],]+ )
|(?<open>\[)
|(?<-open>\])
)+)
(?(open)(?!))
,?
)*
\]
", RegexOptions.IgnorePatternWhitespace);
// Find the first match:
var result = pattern.Match(input);
if (result.Success) {
// Extract the captured values:
var captures = result.Groups["results"].Captures.Cast<Capture>().Select(c => c.Value).ToArray();
return captures;
}
// Not a match
return null;
}
Using this code, you will see that StringToArray("[a, b, [c, [d, e]], f, [g, h], i]") will return the following array: ["a", "b", "[c, [d, e]]", "f", "[g, h]", "i"].
For more information on the balanced groups that I used for matching balanced braces, take a look at Microsoft's documentation.
Update:
As per the comments, if you want to also balance quotes, here's a possible modification. (Note that in C# the " is escaped as "") I also added descriptions of the pattern to help clarify it:
var pattern = new Regex(#"
\[
(?:
\s*
(?<results>(?: # Capture everything into 'results'
(?(open) # If 'open' Then
[^\[\]]+ # Capture everything but brackets
| # Else (not open):
(?: # Capture either:
[^\[\],'""]+ # Unimportant characters
| # Or
['""][^'""]*?['""] # Anything between quotes
)
) # End If
|(?<open>\[) # Open bracket
|(?<-open>\]) # Close bracket
)+)
(?(open)(?!)) # Fail while there's an unbalanced 'open'
,?
)*
\]
", RegexOptions.IgnorePatternWhitespace);
with Regex, it can solve your problem:
static string[] StringToArray(string str)
{
Regex reg = new Regex(#"^\[(.*)\]$");
Match match = reg.Match(str);
if (!match.Success)
return null;
str = match.Groups[1].Value;
List<string> list = new List<string>();
reg = new Regex(#"\[[^\[\]]*(((?'Open'\[)[^\[\]]*)+((?'-Open'\])[^\[\]]*)+)*(?(Open)(?!))\]");
Dictionary<string, string> dic = new Dictionary<string, string>();
int index = 0;
str = reg.Replace(str, m =>
{
string temp = "ojlovecd" + (index++).ToString();
dic.Add(temp, m.Value);
return temp;
});
string[] result = str.Split(',');
for (int i = 0; i < result.Length; i++)
{
string s = result[i].Trim();
if (dic.ContainsKey(s))
result[i] = dic[s].Trim();
else
result[i] = s;
}
return result;
}
Honestly I would just write this method in an F# assembly as its probably much easier. If you look at the JavaScriptSerializer implementation in C# (with a decompiler like dotPeek or reflector) you can see how messy the array parsing code is for a similar array in JSON. Granted this has to handle a much more varied array of tokens, but you get the idea.
Here is their DeserializeList implementation, uglier than it is normally as its dotPeek's decompiled version, not the original, but you get the idea. The DeserializeInternal would recurse down to the child list.
private IList DeserializeList(int depth)
{
IList list = (IList) new ArrayList();
char? nullable1 = this._s.MoveNext();
if (((int) nullable1.GetValueOrDefault() != 91 ? 1 : (!nullable1.HasValue ? 1 : 0)) != 0)
throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayStart));
bool flag = false;
char? nextNonEmptyChar;
char? nullable2;
do
{
char? nullable3 = nextNonEmptyChar = this._s.GetNextNonEmptyChar();
if ((nullable3.HasValue ? new int?((int) nullable3.GetValueOrDefault()) : new int?()).HasValue)
{
char? nullable4 = nextNonEmptyChar;
if (((int) nullable4.GetValueOrDefault() != 93 ? 1 : (!nullable4.HasValue ? 1 : 0)) != 0)
{
this._s.MovePrev();
object obj = this.DeserializeInternal(depth);
list.Add(obj);
flag = false;
nextNonEmptyChar = this._s.GetNextNonEmptyChar();
char? nullable5 = nextNonEmptyChar;
if (((int) nullable5.GetValueOrDefault() != 93 ? 0 : (nullable5.HasValue ? 1 : 0)) == 0)
{
flag = true;
nullable2 = nextNonEmptyChar;
}
else
goto label_8;
}
else
goto label_8;
}
else
goto label_8;
}
while (((int) nullable2.GetValueOrDefault() != 44 ? 1 : (!nullable2.HasValue ? 1 : 0)) == 0);
throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayExpectComma));
label_8:
if (flag)
throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayExtraComma));
char? nullable6 = nextNonEmptyChar;
if (((int) nullable6.GetValueOrDefault() != 93 ? 1 : (!nullable6.HasValue ? 1 : 0)) != 0)
throw new ArgumentException(this._s.GetDebugString(AtlasWeb.JSON_InvalidArrayEnd));
else
return list;
}
Recursive parsing is just not managed as well though in C# as it is in F#.
There is no real "standard" way of doing this. Note that the implementation can get pretty messy if you want to consider all possibilities. I would recommend something recursive like:
private static IEnumerable<object> StringToArray2(string input)
{
var characters = input.GetEnumerator();
return InternalStringToArray2(characters);
}
private static IEnumerable<object> InternalStringToArray2(IEnumerator<char> characters)
{
StringBuilder valueBuilder = new StringBuilder();
while (characters.MoveNext())
{
char current = characters.Current;
switch (current)
{
case '[':
yield return InternalStringToArray2(characters);
break;
case ']':
yield return valueBuilder.ToString();
valueBuilder.Clear();
yield break;
case ',':
yield return valueBuilder.ToString();
valueBuilder.Clear();
break;
default:
valueBuilder.Append(current);
break;
}
Although your not restricted to recursiveness and can always fall back to a single method like
private static IEnumerable<object> StringToArray1(string input)
{
Stack<List<object>> levelEntries = new Stack<List<object>>();
List<object> current = null;
StringBuilder currentLineBuilder = new StringBuilder();
foreach (char nextChar in input)
{
switch (nextChar)
{
case '[':
levelEntries.Push(current);
current = new List<object>();
break;
case ']':
current.Add(currentLineBuilder.ToString());
currentLineBuilder.Clear();
var last = current;
if (levelEntries.Peek() != null)
{
current = levelEntries.Pop();
current.Add(last);
}
break;
case ',':
current.Add(currentLineBuilder.ToString());
currentLineBuilder.Clear();
break;
default:
currentLineBuilder.Append(nextChar);
break;
}
}
return current;
}
Whatever smells good to you
using System;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.FileIO; //Microsoft.VisualBasic.dll
using System.IO;
public class Sample {
static void Main(){
string data = "[a, b, [c, [d, e]], f, [g, h], i]";
string[] fields = StringToArray(data);
//check print
foreach(var item in fields){
Console.WriteLine("\"{0}\"",item);
}
}
static string[] StringToArray(string data){
string[] fields = null;
Regex innerPat = new Regex(#"\[\s*(.+)\s*\]");
string innerStr = innerPat.Matches(data)[0].Groups[1].Value;
StringBuilder wk = new StringBuilder();
var balance = 0;
for(var i = 0;i<innerStr.Length;++i){
char ch = innerStr[i];
switch(ch){
case '[':
if(balance == 0){
wk.Append('"');
}
wk.Append(ch);
++balance;
continue;
case ']':
wk.Append(ch);
--balance;
if(balance == 0){
wk.Append('"');
}
continue;
default:
wk.Append(ch);
break;
}
}
var reader = new StringReader(wk.ToString());
using(var csvReader = new TextFieldParser(reader)){
csvReader.SetDelimiters(new string[] {","});
csvReader.HasFieldsEnclosedInQuotes = true;
fields = csvReader.ReadFields();
}
return fields;
}
}

Categories