正規表現が書けるようになる!
以下の正規表現が読めるようになるはず?!
/^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(?<=\d)[!@#$%^&*]).{8,}/;
順番にチェック強度を上げていく
正規表現を順番に学んでいきましょう!
正規表現を学ぶための題材として、パスワードチェッカーを扱っています。 実際のパスワードバリデーションには、セキュリティの要件やユーザビリティを考慮する必要があります。
Regular Expression (Regex) - 文字列のパターンを表現する記法
/で囲む、もしくはRegExpコンストラクタを使用
/
RegExp
const patternA = /正規表現パターン/; const patternB = new RegExp("正規表現パターン"); patternA.test("検証したい文字列"); // true or false
// 例: 3文字以上の英小文字にマッチ const regex = /^[a-z]{3,}$/; console.log(regex.test("abc")); // true
今回はスラッシュ表記/を使います。
.
/a.c/
*
/ab*/
+
/ab+/
?
/ab?/
{n}
/a{3}/
{n,}
/a{2,}/
{n,m}
/a{2,4}/
※JavaScriptでは{,n}が使えません
{,n}
project/password-checker.js
checkLevel1に正規表現を書いてみよう!
checkLevel1
export function checkLevel1(password) { return /a{4}/.test(password); // 例えば、aが4回連続するかチェック }
色々書いて試してみよう!
角括弧 [] で文字の集合を指定
[]
[abc]
/[abc]/
[a-z]
/[a-z]/
[A-Z]
/[A-Z]/
[0-9]
/[0-9]/
[a-zA-Z]
/[a-zA-Z]/
export function checkLevel1(password) { return /[a-zA-Z0-9]{8,}/.test(password); // 例えば、英数字が8文字以上かチェック }
文字列が特定のパターンを含むかチェック
(?=パターン)
文字列が指定のパターンを含むことを確認
/(?=.*[a-z])/.test("Pass") // true (英小文字を含む) /(?=.*[a-z])/.test("PASS") // false (英小文字を含まない)
肯定先読みの「肯定」: ===のイメージ。反対に「否定」は!==のイメージ。
===
!==
例えば、小文字を含む && 4文字以上のパターンが書ける。
/(?=.*[a-z]).{4}/.test("Pass"); // true
/(?=.*[a])(?=.*[b])/.test("ab"); // true /(?=.*[a])(?=.*[b])/.test("ba"); // true /(?=.*[a])(?=.*[B])/.test("ab"); // false
export function checkLevel1(password) { return /(?!)/.test(password); }
deno run --allow-read --allow-net server.deno.js
localhost:8080
deno test project/password-checker.test.js -- --level=1
export function checkLevel1(password) { return /.{8,}/.test(password); }
{8,}
export function checkLevel2(password) { return /(?!)/.test(password); // TODO }
deno test project/password-checker.test.js -- --level=2
export function checkLevel2(password) { return /(?=.*[a-z]).{8,}/.test(password); }
(?=.*[a-z])
.*
.{8,}
Paaassword1
Password11
( )
\1
(.)\1\1 // 同じ文字が3回連続
(.)
(.)\1
(.)\1\1
(?!パターン)
文字列が指定のパターンを含まないことを確認
/(?!.*[a-z])/.test("Pass") // false (英小文字を含む) /(?!.*[a-z])/.test("PASS") // true (英小文字を含まない)
肯定先読み「(?=パターン)」の反対。
^ // 文字列の開始 $ // 文字列の終了
^
$
export function checkLevel3(password) { return /(?!)/.test(password); // TODO }
deno test project/password-checker.test.js -- --level=3
export function checkLevel3(password) { return /^(?!.*(.)\1\1)(?=.*[a-z]).{8,}/.test(password); }
(?!.*(.)\1\1)
アンカー^がない場合、「連続3文字を含まない英字8文字以上」の文字列にもマッチしてしまう。
例えば、Paaaasword11はaasword11が条件を満たすためマッチしてしまう。
Paaaasword11
aasword11
^(?!.*(.)\1\1) で文字列全体に連続3文字がないことを確認
^(?!.*(.)\1\1)
みじかく書けます。
\d
\w
[A-Za-z0-9_]
\s
[\f\n\r\t\v]
他にもありますが省略。詳しくは末尾の参考資料 MDN 文字クラスへ
export function checkLevel4(password) { return /(?!)/.test(password); // TODO }
deno test project/password-checker.test.js -- --level=4
export function checkLevel4(password) { return /^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}/.test(password); }
(?=.*[A-Z])
(?=.*\d)
^(?!.*(.)\1\1)(?=.*[a-z]).{8,}$
(?<=パターン)
/(?<=\d)[$]/.test("5$"); // true (直前が数字)
(?<=\d)
[$]
export function checkLevel5(password) { return /(?!)/.test(password); // TODO
deno test project/password-checker.test.js -- --level=5
export function checkLevel5(password) { return /^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(?<=\d)[!@#$%^&*]).{8,}/ .test(password); }
(?=.*(?<=\d)[!@#$%^&*])
(?=.*...)
(?<=\d)[!@#$%^&*]
収まらないので正規表現だけ抜粋
/.{8,}/ // レベル1 /(?=.*[a-z]).{8,}/ // レベル2 /^(?!.*(.)\1\1)(?=.*[a-z]).{8,}$/ // レベル3 /^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}/ // レベル4 /^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(?<=\d)[!@#$%^&*]).{8,}/ // レベル5
(?=...)
(?!...)
(?<=...)
()
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/
[0-9]{3}[-]?[0-9]{4}[-]?[0-9]{4}
/ERROR:\s(.*)/