I want to validate a password using regular expression in c#.
These are the conditions :
Others have suggested regex answers for you and maybe that's an immovable constraint of your problem. However, this chunk of C# does what you ask and is different from the regular expressions in that it is far more readable. I would expect a junior level programmer to be able to read it and understand it (and it's also easier to modify it to, say, not return false but return null on success and a string describing how the password fails). It also runs in O(n) (this is a minor thing considering that your typical password will be << 100 characters.
private const int kMinimumLength = 8;
private static string _specialChars = "@#_";
private static bool IsSpecialChar(char c) { return _specialChars.IndexOf(c) >= 0; }
private static bool IsValidPasswordChar(char c) { return IsSpecialChar(c) || Char.IsLetterOrDigit(c); }
public static bool IsPasswordValid(string password)
{
if (password == null || password.Length < kMinimumLength || IsSpecial(password[0])
|| IsSpecial(password[password.Length - 1]))
return false;
bool hasLetter = false, hasDigit = false;
int specials = 0;
foreach (char c in password)
{
hasDigit = hasDigit || Char.IsDigit(c);
hasLetter = hasLetter || Char.IsLetter(c);
specials += IsSpecialChar(c) ? 1 : 0;
if (!IsValidPasswordChar(c)) return false;
}
return hasDigit && hasLetter && specials > 1;
}
Now if you think about this process and understand that in such a small problem domain, you might be better off for readability to do something like this:
public class Rule {
public Func Predicate { get; set; }
public string Description { get; set; }
}
private List rules = new List() {
new Rule(){ Predicate = (s => s != null),
Description = "Password must not be null" },
new Rule(){ Predicate = (s => s.Length >= kMinimumLength ),
Description = "Password must have at least " + kMinimumLength + " characters." },
new Rule(){ Predicate = (s => s.Count(c => IsSpecialChar(c)) >= 1),
Description = "Password must contain at least one of " + _specialChars },
new Rule(){ Predicate = (s => !IsSpecialChar(s[0]) && !IsSpecialChar(s[s.Length - 1])),
Description = "Password must not start or end with " + _specialChars },
new Rule(){ Predicate = (s => s.Count(c => Char.IsLetter(c)) > 0),
Description = "Password must contain at least one letter." },
new Rule(){ Predicate = (s => s.Count(c => Char.IsDigit(c)) > 0),
Description = "Password must contain at least one digit." },
new Rule(){ Predicate = (s =>s.Count(c => !IsValidPasswordChar(c)) == 0),
Description = "Password must contain letters, digits, or one of " + _specialChars }
}
public bool IsPasswordValid(string s, ref string failureReason)
{
foreach (Rule r in rules) {
if (!r.Predicate(s)) {
failureReason = r.Description;
return false;
}
}
return true;
}
And before you start thinking that I've gone all entertrpisey on you, you can look at this code immediately and each rule is self-documenting. It's easy to modify. It's easy to maintain. All the rules are isolated from each other, and if you choose to use static methods for the predicates instead of lambdas, you can easily unit test each rule on its own.
Running this code:
static void Main(string[] args)
{
string reason = null;
if (!IsPasswordValid(null, ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("aaaaaaaa", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("_aaaaaaa", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("aaaaaaa_", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("1aaa!aaa", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("11111111", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("a1a1a1a1", ref reason)) Console.WriteLine(reason);
if (!IsPasswordValid("a1a1@1a1", ref reason)) Console.WriteLine(reason);
StringBuilder sb = new StringBuilder();
sb.Append('a');
for (int i = 0; i < 1000000; i++) { sb.Append('@'); }
sb.Append('a');
sb.Append('1');
string pass = sb.ToString();
long ticks = Environment.TickCount;
if (IsPasswordValid(pass, ref reason)) Console.WriteLine("Valid.");
long endticks = Environment.TickCount;
Console.WriteLine("Time elapsed: " + (endticks - ticks));
}
gives:
Password must not be null
Password must have at least 8 characters.
Password must contain at least one of @#_
Password must not start or end with @#_
Password must not start or end with @#_
Password must contain at least one of @#_
Password must contain at least one of @#_
Password must contain at least one of @#_
Valid.
Time elapsed: 62
So if you're worried about performance, this can check a 1Mb password in 62 milliseconds (on my machine, which is pretty beefy).
tl;dr - "Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems." - Jamie Zawinksi