すぐ忘れるので、正規表現のサンプルを書きまくっております。
パスワードの正規表現を調べていて見つけたものがよくわからなかったので調べました。
参考サイトqiita
なぜElixirなのかは聞かないでください。もう少しメジャーな言語で書いた方が多くの方の参考になりそうですが、正規表現自体はどの言語でも基本は同じなので、まあ良しとしましょう。
基本
# 先頭から探索し、最初に出現する小文字アルファベット1文字
Regex.run(~r/[a-z]/, "abcde")
# -> ["a"]
# 先頭から探索し、最初に出現する小文字アルファベット1文字
Regex.run(~r/[a-z]/, "123abcde")
# -> ["a"]
先頭文字
^
は先頭を表します。
# ^は先頭を表すので、先頭文字が小文字アルファベットの時にマッチ
Regex.run(~r/^[a-z]/, "abcde")
# -> ["a"]
# 先頭文字が小文字アルファベットでないのでマッチしない
Regex.run(~r/^[a-z]/, "123abcde")
# -> nil
末尾文字
$
は末尾を表します。
# $は末尾を表す。末尾が小文字アルファベットの時にマッチ
Regex.run(~r/[a-z]$/, "abcde")
# -> ["e"]
# 末尾が小文字アルファベットでないのでマッチしない
Regex.run(~r/[a-z]$/, "abcde123")
# -> nil
直前のパターンが1回以上
+
と{1,}
と[文字]*[文字]
のパターンが使えます。
# .は改行以外の全ての文字にマッチ
# .{1,}は1文字以上にマッチ
Regex.run(~r/.{1,}/, "abc123ABCあいう!@#$%^&*(),.<>\n改行後")
# -> ["abc123ABCあいう!@#$%^&*(),.<>"]
# .{1,}は1文字以上にマッチ
Regex.run(~r/.{1,}/, "abc\ndef")
# -> ["abc"]
# .+は1文字以上にマッチ(上の正規表現と等価)
Regex.run(~r/.+/, "abc\ndef")
# -> ["abc"]
# 1文字以上の小文字アルファベットにマッチ
Regex.run(~r/[a-z]{1,}/, "ABCabcdef123")
# -> ["abcdef"]
# 0文字以上の小文字アルファベットにマッチ+1文字の小文字アルファベットにマッチ
# これは上の正規表現と等価
Regex.run(~r/[a-z]*[a-z]/, "ABCabcdef123")
# -> ["abcdef"]
# 「おい」が1回以上続くパターンにマッチ
Regex.run(~r/(おい)+/, "え、おい")
# -> ["おい", "おい"]
Regex.run(~r/(おい)+/, "え、おいおいおいおい")
# -> ["おいおいおいおい", "おい"]
直前のパターンが0回以上
*
は0回以上繰り返されるパターンにマッチします。
# 0文字以上の数字があればマッチ
# 従ってあらゆるパターンにマッチしてしまいます
Regex.run(~r/[\d]*/, "ABC123abc")
# -> [""]
# 数字がなくてもマッチする
Regex.run(~r/[\d]*/, "ABCabc")
# -> [""]
0回か1回
?
は直前のパターンが0回か1回にマッチします。
# sample.exとsample_bkp.exにマッチ
Regex.run(~r/^sample(_bkp)?\.ex$/, "sample.ex")
# -> ["sample.ex"]
Regex.run(~r/^sample(_bkp)?\.ex$/, "sample_bkp.ex")
# -> ["sample_bkp.ex", "_bkp"]
貪欲と非貪欲
?
には次のような使い方もあります。
# *?は0文字以上に非貪欲マッチ(マッチした時点で探索を止める = マッチする最小限の文字数)
# 0文字以上の小文字アルファベットに非貪欲マッチ -> あらゆる文字列にマッチ
Regex.run(~r/[a-z]*?/, "ABCabcdef123")
# -> [""]
# 1文字以上の小文字アルファベットに非貪欲マッチ
Regex.run(~r/[a-z]{1,}?/, "ABCabcdef123")
# -> ["a"]
# 上の正規表現と等価
Regex.run(~r/[a-z]*?[a-z]/, "ABCabcdef123")
# -> ["a"]
# 上の2つの正規表現と等価
Regex.run(~r/[a-z]+?/, "ABCabcdef123")
# -> ["a"]
# piyoに貪欲マッチ
Regex.run(~r/(piyo){1,}/, "abcpiyopiyopiyopiyo")
# -> ["piyopiyopiyopiyo", "piyo"]
# piyoに非貪欲マッチ
Regex.run(~r/(piyo){1,}?/, "abcpiyopiyopiyopiyo")
# -> ["piyo", "piyo"]
文字の集合
すでに使っていますが、[文字]
で表します。
# 2文字以上7文字以下のabcの集合にマッチ(順序を問わない)
Regex.run(~r/[abc]{2,7}/, "cb")
# -> ["cb"]
# 上と同じ
Regex.run(~r/[abc]{2,7}/, "abcbacbaaccbb")
# -> ["abcbacb"]
# 集合の文字が一つもないのでマッチしない
Regex.run(~r/[abc]{2,7}/, "dddd")
# -> nil
# 数字とアルファベット大文字・小文字にマッチ
Regex.run(~r/[a-zA-Z0-9]+/, "abc123ABC")
# -> ["abc123ABC"]
or
|
を使ってorを表現できます。
# 末尾の.pyか.cか.exか.hsにマッチ
Regex.run(~r/^.+\.(py|c|ex|hs)$/, "code.py")
# -> ["code.py", "py"]
Regex.run(~r/^.+\.(py|c|ex|hs)$/, "code.c")
# -> ["code.c", "c"]
Regex.run(~r/^.+\.(py|c|ex|hs)$/, "code.ex")
# -> ["code.ex", "ex"]
Regex.run(~r/^.+\.(py|c|ex|hs)$/, "code.hs")
# -> ["code.hs", "hs"]
肯定先読み
(?=パターン)
で表します。
# abcの後に数字が続く場合に、abcにマッチする
Regex.run(~r/abc(?=\d)/, "ABCabc123")
# -> ["abc"]
# abcの後に数字が続かないのでマッチしない
Regex.run(~r/abc(?=\d)/, "ABCabc")
# -> nil
# 拡張子が```.ex```のファイルのファイル名部分にマッチする
Regex.run(~r/^.*(?=\.ex$)/, "file_name.ex")
# -> ["file_name"]
# 小文字アルファベットが最低1文字含まれる文字列にマッチ
# 文字列先頭の後に「改行以外の任意の文字が0文字以上続いた後に小文字アルファベットが1文字続くパターン」がある場合に文字列先頭にマッチするので、1文字も消費しない
Regex.run(~r/^(?=.*[a-z])/, "aABC321あいう")
# -> [""]
# どこにあってもマッチ
Regex.run(~r/^(?=.*[a-z])/, "ABCa321あいう")
# -> [""]
# どこにあってもマッチ
Regex.run(~r/^(?=.*[a-z])/, "ABC321aあいう")
# -> [""]
# どこにあってもマッチ
Regex.run(~r/^(?=.*[a-z])/, "ABC321あいうa")
# -> [""]
否定先読み
“`(?!パターン)で表します。
# abcの後に数字が続かない場合に、abcにマッチする
Regex.run(~r/abc(?!\d)/, "ABCabcあいう")
# -> ["abc"]
# 数字が続くのでマッチしない
Regex.run(~r/abc(?!\d)/, "ABCabc321")
# -> nil
パスワード用正規表現の例
# (?=.*[a-z])は、小文字アルファベットを最低1文字含む条件
# (?=.*[A-Z])は、大文字アルファベットを最低1文字含む条件
# (?=.*\d)は、数字を最低1文字含む条件
# {10,20}$は、10文字以上20文字以内の条件
Regex.run(~r/^(?=.*?[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{10,20}$/, "ABC321abcd")
# -> ["ABC321abcd"]