とあるWeb制作会社にて
ワイ「社長、こないだ頼まれたショッピングサイトの件なんですけど」
ワイ「ご注文フォームのコーディング、完了しましたで!」
社長「おお、ありがとうな」
ワイ「ほな、飲みに行ってきますわ!」
社長「・・・いや待てや(まだ15時やし)」
社長「何やこのフォーム」
ワイ「何ですかいな」
ワイ「デザイン通り、完璧にコーディングできてますやん」
社長「いやバリデーションが全くされとらへんがな」
ワイ「グラデュエーション?」
ワイ「何を卒業するんでっか」
社長「バリデーションや」
社長「貴様をこの会社から卒業させたろか」
バリデーションとは
社長「バリデーションちゅうのは、不正な値やないかどうか確認することや」
社長「例えば数字を入力してほしいフォームに、文字列を入力して送信されても困るやろ?」
社長「そういうのをちゃんとチェックするのがフォームバリデーションや」
ワイ「あ、ああ・・・そっちのバリデーションでっか」
社長「お前の作ったこのフォームを見てみいや」
社長「数量のところに**ウン
個って入力しても、そのまま注文画面に進めてしまうやないか」
社長「お客様に何てモノ**をお届けするつもりや」
ワイ「(いや、そのお客様はむしろ欲しがってますやん)」
社長「今日は飲みに行ってええから、明日ちゃんとバリデーション機能も実装してくれや」
ワイ「おお・・・ホワイト会社や・・・」
ワイ「お先に失礼します・・・」
翌日
ワイ「なあ、ハスケル子ちゃん」
ワイ「昨日ワイが作ったご注文フォームなんやけど」
ワイ「フォーム・バ・リデーションができてないからダメやって」
ハスケル子「そ、そうですか」
ハスケル子「(フォーム・バ・リデーション・・・?)」
ハスケル子「でもまあ、そりゃそうですよね」
ハスケル子「確か、仕様書にも」
各入力項目にバリデーションを掛けてください
ハスケル子「って書いてありましたよ」
ワイ「うん」
ワイ「よう分からんかったから、背景に薄くグラデーションは掛けておいたんやけどね」
ワイ「やっぱアカンかったわ」
バリデーション開始
ワイ「さあ、バ・リデーションをしていこか」
ワイ「まずは数量のバ・リデーションや」
ワイ「数値以外が入ってたらNGにしたい場合は・・・」
ワイ「確か正規表現いうのを使ってやるんや」
const isValid = /正規表現/.test(userInput);
ワイ「↑こんな感じでtest
メソッドを使ってやればisValid
に真偽値が入ってくるはずや」
ワイ「userInput
にはユーザーが入力した文字列が入ってくる想定やで」
ワイ「せやから・・・」
const isValid = /[0-9]+/.test(userInput);
ワイ「↑こう書いてやればええな」
ワイ「[0-9]+
、つまり0~9の中のいずれかの文字が1個以上ありまっせ、ってことや」
ワイ「+
は、直前の文字が1回もしくはそれ以上繰り返されるって意味やからな」
ワイ「よっしゃ完璧や」
ハスケル子「待ってください」
「含む」になっちゃう
ハスケル子「/[0-9]+/
という正規表現だと」
ハスケル子「0〜9の繰り返しを**『含む』って意味になっちゃうので」
ハスケル子「↓こんな文字列でもOK**になっちゃいますよ」
- 5万
- ほげ10
- ワイが1番や!!!
ワイ「Oh...」
ハスケル子「なので正規表現は/^[0-9]+$/
の方がいいですね」
ハスケル子「^
ていうのは**〜から始まるって意味で」
ハスケル子「$
ていうのは〜で終わる**って意味です」
ハスケル子「今回の例で言うと」
0〜9の繰り返しで始まって、そのまま終わる文字列
ハスケル子「って感じです」
ハスケル子「つまり^
と$
で挟むと部分一致ではなく完全一致になるってことですね」
ワイ「おお、ありがとう」
ワイ「危ない危ない・・・」
ワイ「ウ〇コが10
個、とかでもOKになってしまうところやったわ・・・」
ワイ「配達員さんも困るで」
ハスケル子「ちなみにこの正規表現だと、1文字目が0
でもOK扱いになります」
ハスケル子「なので、001
とかもOKになりますね」
ハスケル子「それをNGにしたい場合は」
ハスケル子「/^[1-9][0-9]*$/
」
ハスケル子「↑こうですね」
- 最初に1〜9が1つある →
^[1-9]
- その後ろに0〜9が0個以上ある →
[0-9]*
- そのまま終わる →
$
ワイ「そっかそっか」
ワイ「*
は、直前の文字の0回以上の繰り返しってことやもんな」
ワイ「なるほどなぁ〜」
繰り返しの回数を指定する
ワイ「次は姓・名を入力するフォームのバリデーションやな」
ワイ「ええと、姓の入力ルールとしては」
ワイ「3文字以内であること・・・それくらいチェックしておけばええかな」
ハスケル子「いや勅使河原さんとかどうするんですか」
ハスケル子「仕様書ちゃんと読んでください」
ハスケル子「姓は**1〜30文字
**って書いてありますよ」
ワイ「おお、ありがとうやで」
ワイ「30文字以内ってのはどうやって調べればええんやろ・・・」
ハスケル子「ええと」
- 任意の文字 →
.
- それが1〜30個あること →
{1,30}
- 「含む」ではなく「完全一致」 →
^
と$
で挟む
ハスケル子「って感じだから」
const isValid = /^.{1,30}$/.test(userInput);
ハスケル子「↑こうですね」
ワイ「今回も完全一致なんやね」
ハスケル子「はい」
ハスケル子「だって**『含む』だと35文字でもOK**になってしまいますからね」
ワイ「なるほどや」
ハスケル子「でも、ここは無理に正規表現を使う必要ないかもですね」
ワイ「ん?どういうこと?」
無理に正規表現で頑張る必要はない
ハスケル子「つまり↓こんな感じで書いてもいいってことです」
const isValid = 0 < userInput.length && userInput.length <= 30;
ハスケル子「単にlength
で済みますからね」
ハスケル子「無理に正規表現で頑張るより」
ハスケル子「JSの便利なメソッドたちを活用していきましょう」
ワイ「なるほどな」
ハスケル子「ちなみにサロゲートペア1のことも考えるなら」
ハスケル子「userInput.length
より」
ハスケル子「[...userInput].length
の方がいいですね」
ワイ「デリケートビア・・・」
ワイ「ああ、腐りやすいビールのことやな?」
ハスケル子「サロゲートペアです」
ハスケル子「絵文字や一部の漢字は4バイト文字なので、1文字で2文字分にカウントされちゃう、ってやつです」
ワイ「なるほどな」
ワイ「最近は絵文字を含んだ苗字の人もおるもんな」
ハスケル子「そうですか」
ワイ「(相変わらずツッコミ冷たいな・・・)」
ハスケル子「(腐ったビールでも飲んでればいいのに)」
指定した選択肢の中の文字列のみOKにする
ワイ「次は、年号のバリデーションやな」
ワイ「大正・昭和・平成・令和だけをOKにしたいんや」
ワイ「せやから・・・」
const isValid = /^大正$|^昭和$|^平成$|^令和$/.test(userInput);
ワイ「↑こうやな」
ワイ「記号の|
は**『または』**って意味やから」
^大正$
- または
^昭和$
- または
^平成$
- または
^令和$
ワイ「ってことや」
ハスケル子「それでもいいですけど、もっと短く書けますよ」
ワイ「そうなん?」
ワイ「えーと」
ワイ「/^大正|昭和|平成|令和$/
とか?」
ハスケル子「惜しいですね」
ハスケル子「それだと」
大正
から始まる文字列- または
昭和
を含む文字列- または
平成
を含む文字列- または
令和
で終わる文字列
ハスケル子「上記の全部に当てはまってしまいます」
ハスケル子「つまり」
- 大正生まれやで!
- 多分平成かなぁ
- キラキラの令和
ハスケル子「↑こんな文字列までOKになっちゃいます」
ワイ「ガバガバやん・・・」
ワイ「^
や$
の方が、|
より先に評価されてしまうんやな・・・」
ハスケル子「はい」
ハスケル子「なので」
const isValid = /^(大正|昭和|平成|令和)$/.test(userInput);
ハスケル子「↑こうですね」
ワイ「^(大正|昭和|平成|令和)$
かぁ」
ワイ「括弧をつけるんやね」
ハスケル子「括弧でグループ化すると」
ハスケル子「その中は先に評価してもらえます」
ハスケル子「なので・・・」
大正
または昭和
または平成
または令和
^
と$
で挟んであるので「含む」ではなく「完全一致」
ハスケル子「ちゃんと↑こういう意味に評価してくれるんです」
ハスケル子「でも、この場合も無理に正規表現にしなくてもいいですね」
const isValid = ["大正", "昭和", "平成", "令和"].includes(userInput);
ハスケル子「↑これでいいわけですからね」
否定先読み
ワイ「さて次は」
無職
から始まる文字列であること- ただし
無職やめ太郎
から始まる文字列はNG
ワイ「こういうバ・リデーションや」
ハスケル子「(どんなご注文フォームなんだろう)」
ハスケル子「(やめ太郎さん、出禁にされてるのかな・・・)」
ワイ「こういう否定みたいなのは正規表現でどうやって表すんやろ」
ハスケル子「ええと」
const isValid = /^無職(?!やめ太郎)/.test(userInput);
ハスケル子「↑こうですね」
ハスケル子「(?!やめ太郎)
は否定先読みっていう書き方です」
無職
から始まる →^無職
- ただし後ろに
やめ太郎
が続くのはNG →(?!やめ太郎)
ハスケル子「↑こういう意味です」
否定後読み
ワイ「次は」
太郎
を含むこと- ただし
やめ太郎
はNG
ワイ「↑この条件や」
ハスケル子「(これは厳し目な出禁だな・・・)」
const isValid = /(?<!やめ)太郎/.test(userInput);
ワイ「↑こうやな!」
ハスケル子「合ってますけど、その否定後読みはES2018で追加されたものなので」
ハスケル子「FirefoxやSafariで使えないですね・・・」
ワイ「おお、そうなんか・・・」
ハスケル子「なので」
const isValid = /太郎/.test(userInput) && !/やめ太郎/.test(userInput);
ハスケル子「こんな感じにしておきましょう」
ワイ「おお、2つ組み合わせるのもええね」
ほかにも色々
ワイ「次は」
- 1st, 2nd, 3rd, 4th
- 11th, 12th, 13th, 14th
- 21st, 22nd, 23rd, 24th
ワイ「↑こんなやつだけをOKにしたいんや!」
ワイ「英語の序数ってやつやな」
ハスケル子「ええと」
const isValid = /^(([0-9]*[02-9])?(1st|2nd|3rd)|([0-9]*([04-9]|1[1-3])th))$/.test(userInput) && /^[1-9]/.test(userInput);
ハスケル子「↑こうですね」
ワイ「おお」
ワイ「けっこう長いな」
ハスケル子「はい」
ハスケル子「日本語で説明すると」
- 1文字以上の数字の後ろに
st
,nd
,rd
,th
がつく形式
ハスケル子「↑ざっくりこんなルールですよね」
ハスケル子「細かく定義すると・・・」
- 下1桁が
1
の時は末尾にst
がつくべき- 下1桁が
2
の時は末尾にnd
がつくべき- 下1桁が
3
の時は末尾にrd
がつくべき- ただし下2桁が
11,12,13
の時はth
がつくべき- それ以外の場合は末尾に
th
がつくべき- 1~9から始まるべき(0からは始まらない)
ハスケル子「↑こんなルールですよね」
ワイ「せやな」
ハスケル子「1桁なら割とシンプルで」
const isValid = /^(1st|2nd|3rd|[4-9]th)$/.test(userInput);
ハスケル子「↑こんな感じです」
ワイ「なるほどな」
1st
または2nd
または3rd
のパターン →1st|2nd|3rd
- または4〜9の後ろに
th
がつくパターン →[4-9]th
- それを
^()$
に入れて完全一致にする
ワイ「↑ってことやな」
ハスケル子「はい」
ハスケル子「それが2桁以上になると」
- ただし下2桁が
11,12,13
の時はth
がつくべき- 1~9から始まるべき(0からは始まらない)
ハスケル子「↑こんなルールが加わるので」
const isValid = /^(([0-9]*[02-9])?(1st|2nd|3rd)|([0-9]*([04-9]|1[1-3])th))$/.test(userInput);
ハスケル子「↑こんなに長くなります」
ワイ「う〜ん・・・」
ワイ「11,12,13
の場合は11st,12nd,13rd
やなくて11th,12th,13th
になるわけやから・・・」
1st,2nd,3rd
の前に1は来ない。つまり0または2〜9が来る。
→[02-9]
- 下1桁が0または4〜9の時は
th
がつく。または下2桁が11,12,13
の時もth
がつく。
→([04-9]|1[1-3])th
ワイ「↑ってことかぁ・・・」
ハスケル子「はい」
ハスケル子「でも、これでもまだ・・・」
- 1~9から始まるべき(0からは始まらない)
ハスケル子「↑このルールが満たせていません」
ワイ「まじか・・・」
ハスケル子「そこも全て正規表現で表そうとすると、更に長くなってしまうので・・・」
&& /^[1-9]/.test(userInput);
ハスケル子「↑この条件を加えることで0始まりをブロックしています」
ワイ「なるほどな」
- 1〜9から始まらなあかんよ →
^[1-9]
ワイ「↑ってことやな」
ハスケル子「はい」
ハスケル子「今回は割と長めの正規表現で頑張りましたが」
ハスケル子「実際にはもっと分割して&&
で繋いだ方がいいですね」
ワイ「なるほどなぁ」
ワイ「実務のコードで長〜い正規表現を書かれても、読まれへんもんな・・・」
正規表現完全理解ワイ
ワイ「とにかく」
ワイ「正規表現、完全に理解したわ」
ハスケル子「これでやめ太郎さんも正規表現いっぱい書けますね!」
ワイ「おう!」
ワイ「理論上は可能や!」
何でそんなにできるようになったん?
ワイ「ていうかハスケル子ちゃん」
ワイ「何でそんなに正規表現できるようになったん?」
ハスケル子「このサイトで練習しました」
ワイ「おお」
ワイ「正規表現を試しに書いて」
ワイ「実際にどういった文字列が該当するのかが分かるんやな」
ワイ「ワイもこのサイトで勉強してみるわ・・・!」
やめ太郎の戦いは続く・・・
他にもおすすめサイト
- [uhyohyo.net JavaScript初級者から中級者になろう]
(https://uhyohyo.net/javascript/4_2.html) - 正規表現 - JavaScript | MDN
-
絵文字や一部の漢字はサロゲートペア文字と呼ばれる4バイト文字なため、1文字で2文字とカウントされてしまいます。 ↩