パスワードのバリデーションなどで、こんな正規表現を見かけることがあります。
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/
最初に見たとき、特に分からなかったのが、
(?=.*[a-z])
の部分でした。
「.*は0文字以上なのに、どうして小文字があると判定できるんだろう?」
今回は、ここを一つずつ分解してみます。
(?=...)は「この先にある?」を確認する
(?=...)は**肯定先読み(Positive Lookahead)**と呼ばれます。
簡単に言うと、
「現在位置から先が、この条件を満たしているか?」
を確認する構文です。
特徴は、条件を確認しても文字列自体は読み進めないことです。
そのため、
(?=.*[a-z])
(?=.*[A-Z])
(?=.*[0-9])
のように並べて、
小文字がある?
大文字がある?
数字がある?
と複数の条件を確認できます。
.*は何をしている?
次に、
.*
を分解します。
. → 任意の1文字
* → 直前のパターンを0回以上繰り返す
つまり.*は、
「任意の文字が0文字以上続く」
という意味です。
そのため、
(?=.*[a-z])
は、
「途中に何文字あってもいいので、その先に小文字があるか?」
を確認しています。
小文字を要求しているのは.*ではなく、最後の[a-z]です。
なぜ.+ではなく.*?
ここも少し迷いました。
*と+には次の違いがあります。
* → 0回以上
+ → 1回以上
例えば、
^(?=.*[a-z])
なら、文字列の先頭が小文字でも、
.* → 0文字
[a-z] → 先頭のa
として判定できます。
一方、.+にすると小文字の前に最低1文字必要になります。
「どこかに小文字があるか」を確認したいなら、0文字も許容する.*が適しています。
/^[a-z]+$/とは何が違う?
こちらも似ていますが意味は別です。
/^[a-z]+$/
これは、
「先頭から末尾まで、すべて小文字」
という条件です。
一方、
^(?=.*[a-z])
は、
「文字列のどこかに小文字が1文字以上ある」
という条件だけを確認します。
まとめ
(?=...) → この先が条件を満たすか確認
. → 任意の1文字
* → 0回以上
[a-z] → 小文字1文字
つまり、
(?=.*[a-z])
は、
「この先のどこかに小文字があるか?」
という意味になります。
正規表現は丸ごと覚えようとすると難しいですが、記号を一つずつ分解すると、かなり理解しやすくなりました。