入力を検証する

このレッスンで分かること

  • ^$ で文字列の全体を縛る書き方
  • メールアドレスを「実務で使う程度」に検証するパターンの組み立て方
  • g フラグ付きの正規表現を test で使い回すと 2 回目が落ちる理由

入力を検証する とは

フォームに入った文字が期待した形かどうかを、正規表現で判定することです。全体を縛る ^$ が要になります。

一部に当たるか、全体が当たるか

前のレッスンの /\d{4}/ は「4 個並んだ数字がどこかにある」という意味でした。検証ではこれでは足りません。郵便番号の入力欄に あ1234い と入れられても true になってしまうからです。

文字列の先頭と末尾を指定する記号が用意されています。

記号意味
^文字列の先頭
$文字列の末尾

両方で挟むと「全体がこの形であること」になります。

console.log(/\d{4}/.test("あ1234い")); // true console.log(/^\d{4}$/.test("あ1234い")); // false console.log(/^\d{4}$/.test("1234")); // true

検証で使う正規表現は、ほぼ必ず ^ で始まり $ で終わります。逆に言うと、^$ が無い検証用パターンを見かけたら、まず疑ってよい場所です。

^ は角かっこの中では「以外」を意味しました。[^abc]^abc は別物です。角かっこの外に出てきた ^ は先頭を表す、と覚えると混ざりません。

空白だけの入力をはじく

入門のフォームでは name.length === 0 で空を弾きました。ところがこれは、半角スペースだけを入れられると通ってしまいます。

const name = " "; console.log(name.length === 0); // false 通ってしまう

「全体が空白文字 0 個以上」という形にすれば、空文字列とスペースだけの両方を 1 本で弾けます。

const blank = /^\s*$/; console.log(blank.test("")); // true console.log(blank.test(" ")); // true console.log(blank.test("山田 太郎")); // false

\s は空白文字 1 個、* は 0 個以上なので、^\s*$ は「先頭から末尾まで空白文字しか無い」を表します。空文字列も 0 個として当たるところがうまい点です。

メールアドレスをどこまで見るか

ここで正直に書いておきます。メールアドレスの正しさを正規表現だけで判定することはできません。

理由は 2 つあります。1 つは、メールアドレスの仕様が想像よりはるかに複雑で、引用符やコメントまで許しているため、仕様どおりの正規表現は数百文字になり、書いても誰も読めないことです。もう 1 つは、形が合っていてもそのアドレスが実在するとは限らないことです。taro@example.com は形として完璧ですが、届くかどうかは送ってみないと分かりません。

実務では、次のように割り切ります。

段階何をするか
入力時明らかな打ち間違いを弾く。@ が無い、ドットが無い、空白が入っている
送信後確認メールを送り、リンクを踏んでもらって初めて有効とみなす

正規表現が担当するのは 1 段目だけです。そのつもりで、短いパターンを書きます。

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; console.log(emailPattern.test("taro@example.com")); // true console.log(emailPattern.test("taro@example")); // false console.log(emailPattern.test("taroexample.com")); // false console.log(emailPattern.test("ta ro@example.com")); // false

読み方は次のとおりです。[^\s@]+ は「空白でも @ でもない文字が 1 個以上」です。それが @ を挟んで前後にあり、後ろ側にはさらに \. で区切られた部分がある、という形です。

つまり「アットマークがちょうど 1 個あって、その後ろにドットがある」だけを見ています。これで打ち間違いの大半は捕まえられますし、正しいアドレスを誤って弾く事故は起きません。

検証のパターンは、厳しくするほど正しい入力を弾く危険が上がります。弾かれた利用者は原因が分からないので、そのまま離脱します。迷ったらゆるいほうに倒すのが定石です。

よく使う検証パターン

手を動かす前に、形が決まっているものをいくつか見ておきます。どれも ^$ で挟んである点は同じです。

見たいものパターン当たる例
郵便番号/^\d{3}-\d{4}$/150-0001
半角英数字だけ/^[A-Za-z0-9]+$/taro2026
全体が空白だけ/^\s*$/
4 桁の西暦/^\d{4}$/2026
console.log(/^\d{3}-\d{4}$/.test("150-0001")); // true console.log(/^\d{3}-\d{4}$/.test("1500001")); // false

ハイフンの有無まで縛るかどうかは、入力欄の作り方しだいです。ハイフン無しでも受けたいなら /^\d{3}-?\d{4}$/ のように ? を足します。? は 0 個か 1 個という意味なので、これだけで両方を通せます。

日本語の入力欄では、利用者が全角数字を打つことがあります。\d は半角数字しか見ないので、150-0001 は弾かれます。弾いてメッセージを出すのか、こちらで半角に直してから見るのかは、検証を書く前に決めておく話です。

エラーメッセージをまとめて返す

課題では、入力オブジェクトを受け取ってエラーメッセージの配列を返す関数を書きます。配列にするのは、複数の問題を一度に伝えるためです。1 つ直すたびに次のエラーが出てくる画面は、直しているほうが疲れます。

const errors = validateContact({ name: "", email: "taro" }); console.log(errors); // ["お名前を入力してください", "メールアドレスの形式が正しくありません"]

問題が無ければ空配列を返します。呼ぶ側は errors.length === 0 で成否を判定できます。

よくある間違い

g フラグを付けた正規表現を test で使い回す

これは知らないと必ず一度は踏みます。g フラグ付きの正規表現オブジェクトは lastIndex という「次にどこから探すか」を内部に持っていて、test が当たるたびにそれを進めます。関数の外に置いて使い回すと、2 回目の呼び出しが前回の続きから探し始めて false になります。

// NG 関数の外に g 付きで置いた const pattern = /\d{4}/g; function hasYear(text) { return pattern.test(text); } console.log(hasYear("2026年")); // true console.log(hasYear("2026年")); // false 同じ引数なのに結果が変わる

対処は 2 つあります。検証に g は不要なので付けないのが一番です。どうしても付けるなら、関数の中で毎回作り直します。

// OK 検証に g は要らない const pattern = /\d{4}/; function hasYear(text) { return pattern.test(text); }

^$ を付け忘れる

付け忘れると、途中に混ざっていれば通る判定になります。

console.log(/[^\s@]+@[^\s@]+\.[^\s@]+/.test("問い合わせ先 taro@example.com まで")); // true console.log(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test("問い合わせ先 taro@example.com まで")); // false

空文字列を弾けていると思い込む

+ は 1 個以上なので空文字列は弾けますが、* は 0 個以上なので通します。空の入力を弾くつもりで * を書くと素通りします。

console.log(/^\w*$/.test("")); // true 弾けていない console.log(/^\w+$/.test("")); // false こちらが正しい

要件

  1. 関数名は validateContact、引数は { name, email } の形のオブジェクトを 1 つ受け取る
  2. name が空文字列または空白文字だけなら「お名前を入力してください」を入れる
  3. email が /^[^\s@]+@[^\s@]+.[^\s@]+$/ に当たらなければ「メールアドレスの形式が正しくありません」を入れる
  4. 戻り値は文字列の配列。両方に問題があれば name のメッセージを先に、問題が無ければ空配列を返す

入出力例

validateContact({"email":"taro@example.com","name":"山田 太郎"})[] validateContact({"email":"taro@example.com","name":""})["お名前を入力してください"] validateContact({"email":"taro@example.com","name":" "})["お名前を入力してください"] validateContact({"email":"taro@example","name":"山田 太郎"})["メールアドレスの形式が正しくありません"] validateContact({"email":"taroexample.com","name":""})["お名前を入力してください","メールアドレスの形式が正しくありません"]

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.js
main.js
学習モード

メモ

入力を検証する

⌘S で保存