Semua jawaban yang diberikan sebelumnya menggunakan teknik yang sama (benar) untuk menggunakan lookahead terpisah untuk setiap persyaratan. Tetapi mereka mengandung beberapa inefisiensi dan bug yang berpotensi besar, tergantung pada bagian belakang yang benar-benar akan menggunakan kata sandi.
Saya akan mulai dengan regex dari jawaban yang diterima:
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
Pertama-tama, karena Java mendukung \A
dan \z
saya lebih suka menggunakannya untuk memastikan seluruh string divalidasi, terlepas dari Pattern.MULTILINE
. Ini tidak memengaruhi kinerja, tetapi menghindari kesalahan saat ekspresi reguler didaur ulang.
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}\z
Memeriksa bahwa kata sandi tidak mengandung spasi dan memeriksa panjang minimumnya dapat dilakukan dalam sekali jalan dengan menggunakan semua sekaligus dengan meletakkan pembilang variabel {8,}
pada singkatan \S
yang membatasi karakter yang diizinkan:
\A(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])\S{8,}\z
Jika kata sandi yang diberikan memang mengandung spasi, semua pemeriksaan akan dilakukan, hanya untuk pemeriksaan terakhir gagal pada spasi. Ini dapat dihindari dengan mengganti semua titik dengan \S
:
\A(?=\S*[0-9])(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[@#$%^&+=])\S{8,}\z
Titik hanya boleh digunakan jika Anda benar-benar ingin mengizinkan karakter apa pun. Jika tidak, gunakan kelas karakter (dinegasikan) untuk membatasi regex Anda hanya untuk karakter yang benar-benar diizinkan. Meskipun dalam kasus ini tidak ada bedanya, tidak menggunakan titik saat ada hal lain yang lebih sesuai adalah kebiasaan yang sangat baik. Saya melihat terlalu banyak kasus catastrophic backtracking karena pengembang terlalu malas untuk menggunakan sesuatu yang lebih sesuai daripada titik.
Karena ada kemungkinan besar pengujian awal akan menemukan karakter yang sesuai di paruh pertama kata sandi, penghitung malas bisa lebih efisien:
\A(?=\S*?[0-9])(?=\S*?[a-z])(?=\S*?[A-Z])(?=\S*?[@#$%^&+=])\S{8,}\z
Tetapi sekarang untuk masalah yang sangat penting: tidak ada jawaban yang menyebutkan fakta bahwa pertanyaan asli tampaknya ditulis oleh seseorang yang berpikir dalam ASCII. Tetapi dalam string Java adalah Unicode. Apakah karakter non-ASCII diperbolehkan dalam sandi? Jika ya, hanya spasi ASCII yang tidak diizinkan, atau semua spasi Unicode harus dikecualikan.
Secara default \s
hanya mencocokkan ruang putih ASCII, jadi kebalikannya \S
cocok dengan semua karakter Unicode (spasi atau tidak) dan semua karakter ASCII non-spasi. Jika karakter Unicode diizinkan tetapi spasi Unicode tidak diperbolehkan, UNICODE_CHARACTER_CLASS
bendera dapat ditentukan untuk \S
mengecualikan spasi kosong Unicode. Jika karakter Unicode tidak diperbolehkan, maka [\x21-\x7E]
dapat digunakan sebagai pengganti \S
untuk mencocokkan semua karakter ASCII yang bukan spasi atau karakter kontrol.
Yang membawa kita ke masalah potensial berikutnya: apakah kita ingin mengizinkan karakter kontrol? Langkah pertama dalam menulis regex yang tepat adalah menentukan dengan tepat apa yang ingin Anda cocokkan dan apa yang tidak. Satu-satunya jawaban yang 100% benar secara teknis adalah bahwa spesifikasi kata sandi dalam pertanyaan itu ambigu karena tidak menyatakan apakah rentang karakter tertentu seperti karakter kontrol atau karakter non-ASCII diizinkan atau tidak.