Усі раніше задані відповіді використовують один і той же (правильний) прийом, щоб використовувати окрему підказку для кожної вимоги. Але вони містять пару неефективності та потенційно масову помилку, залежно від зворотного кінця, який фактично використовуватиме пароль.
Почну з регулярного вираження з прийнятої відповіді:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
Перш за все, оскільки Java підтримує, \Aі \zя вважаю за краще використовувати їх, щоб переконатися, що вся нитка перевірена, незалежно від Pattern.MULTILINE. Це не впливає на продуктивність, але дозволяє уникнути помилок при переробці регулярних виразів.
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z
Перевірка того, що пароль не містить пробілу, і перевірка його мінімальної довжини може бути виконана за один прохід, використовуючи всі одразу, додавши змінний кількісний показник {8,}на скорочення, \Sщо обмежує дозволені символи:
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
Якщо вказаний пароль містить пробіл, всі перевірки будуть зроблені, лише щоб остаточна перевірка не була пропущеною. Цього можна уникнути, замінивши всі крапки на \S:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z
Точку слід використовувати лише в тому випадку, якщо ви дійсно хочете дозволити будь-який символ. В іншому випадку використовуйте (заперечуваний) клас символів, щоб обмежити ваш регулярний вираз лише тими символами, які дійсно дозволені. Хоча в цьому випадку мало значення, не дуже корисно використовувати не крапку, коли щось інше є більш підходящим . Я бачу занадто багато випадків катастрофічного зворотного відстеження, оскільки розробник був лінивий, щоб використовувати щось більш відповідне, ніж крапка.
Оскільки є хороший шанс, що початкові тести знайдуть відповідний символ у першій половині пароля, лінивий кількісний показник може бути більш ефективним:
\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z
Але тепер для справді важливого питання: жодна з відповідей не згадує про те, що оригінальне запитання, здається, написав хтось, хто думає в ASCII. Але в Java рядки є Unicode. Чи допускаються символи, що не належать до ASCII, у паролях? Якщо вони є, заборонені лише пробіли ASCII або слід виключити весь пробіл Unicode.
За замовчуванням \sвідповідає лише пробіл ASCII, тому його обернена \Sвідповідність усім символам Unicode (пробіл чи ні) та всі символи ASCII, що не пробіли. Якщо символи Unicode дозволені, але пробіли Unicode - ні, UNICODE_CHARACTER_CLASSпрапор можна вказати, щоб \Sвиключити пробіл Unicode. Якщо символи Unicode заборонені, [\x21-\x7E]їх можна використовувати замість того, \Sщоб відповідати всім символам ASCII, які не є пробілом або символом управління.
Що приводить нас до наступного потенційного питання: чи хочемо ми дозволити контрольні символи? Перший крок у написанні правильного регулярного вираження - точно вказати, що ви хочете відповідати, а що - ні. Єдина стовідсотково технічно правильна відповідь - це те, що специфікація пароля у запитанні є неоднозначною, оскільки в ній не вказано, чи дозволені чи ні певні діапазони символів, такі як контрольні символи чи символи, що не належать до ASCII.