Другие предложили вам регулярные ответы и, возможно, это неотъемлемое ограничение вашей проблемы. Однако этот кусок C# делает то, что вы просите, и отличается от обычных выражений тем, что он гораздо читабельнее. Я бы ожидал, что программист младшего уровня сможет его прочитать и понять (и его также легче модифицировать, скажем, не возвращать false, а возвращать null на успех и строку, описывающую, как сбой пароля). Он также работает в O (N) (это незначительная вещь, учитывая, что ваш типичный пароль будет < < 100 символов.
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;
}
Теперь, если вы думаете об этом процессе и понять, что в такой маленькой проблемной области, вы могли бы быть лучше для удобства чтения, чтобы сделать что-то вроде этого:
public class Rule {
public Func<string, bool> Predicate { get; set; }
public string Description { get; set; }
}
private List<Rule> rules = new List<Rule>() {
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;
}
и прежде, чем вы начнете думать, что я пошел все entertrpisey на вас, вы можете посмотреть на это сразу коде и каждое правило самодокументированно. Его легко модифицировать, он прост в обслуживании. Все правила изолированы друг от друга, и если вы решите использовать stat ic для предикатов, а не лямбда, вы можете легко выполнить каждое правило самостоятельно.
Выполнение этого кода:
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("[email protected]", 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));
}
дает:
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
Так что если вы беспокоитесь о производительности, это может проверить пароль 1Mb в 62 миллисекунд (на моей машине, которая является довольно мускулистый).
tl; dr - «Некоторые люди, столкнувшись с проблемой, думают« Я знаю, я буду использовать регулярные выражения ». Теперь у них есть две проблемы». - Jamie Zawinksi
простой метод C# будет легче читать, проще в обслуживании. –
Не отвечаю на ваш вопрос, но просто хотел упомянуть этот инструмент, который очень полезен для тестирования регулярных выражений ... http://gskinner.com/RegExr/ – KristoferA
Прошу прощения за то, что вы немного не по теме, но это правила 1, 2 и вторая часть 3 («никакие другие специальные символы не разрешены») действительно необходимы? Лично я ненавижу ограничения паролей! И я не вижу причины для них. – gehho