← 記事一覧に戻る
テクノロジー

全角・空白・Unicodeの正規化は入口で1回だけ

全角・空白・Unicodeの正規化は入口で1回だけ

「検索しても出てこない」「同じメールアドレスで二重登録できた」。こうした報告の原因は、ロジックではなく入力値の揺れであることが少なくありません。全角数字、前後の空白、見た目が同じ別の文字。これらを実装の入口でどう揃えるかは、実装フェーズで決めておく設計判断です。

見た目は同じなのに別の値になる例

  • 「09012345678」と「09012345678」: 全角と半角で一致しない
  • 「taro@example.com 」: 末尾の空白つきでユーザー検索に引っかからない
  • 「Taro@Example.com」と「taro@example.com」: 大文字小文字違いで二重登録できる
  • 「が」を1文字で表した場合と「か」+濁点で表した場合: 見た目は同じでも比較すると不一致になる

正規化の対象一覧

  • 全角・半角: 数字、英字、記号、半角カナ
  • 空白: 前後の空白、全角スペース、ゼロ幅スペース
  • 改行コード: CRLF と LF の混在
  • Unicode 正規化: 結合文字(濁点の分離など)
  • 大文字小文字: メールアドレスやユーザー名など、区別しない項目のみ

正規化は入口の1か所に絞る

各画面や各処理で個別に整えると、1か所の漏れが重複登録や検索漏れになります。API の受け口(コントローラやフォームの送信処理)で1回だけ行い、以降は正規化済みの値だけを扱います。

export function normalizeText(input: string): string {
  return input
    .normalize("NFKC")                  // 全角半角・結合文字を統一
    .replace(/\r\n?/g, "\n")            // 改行を LF に
    .replace(/[​-‍]/g, "") // ゼロ幅文字を除去
    .trim();                            // 前後の空白を除去
}

export function normalizeEmail(input: string): string {
  return normalizeText(input).toLowerCase();
}

NFKC は全角空白(U+3000)も半角空白に変えるため、trim の前に通すのがポイントです。

保存値と表示値を分けるべきケース

NFKC は「①」を「1」に、「㈱」を「(株)」に変えます。氏名や商品名など、入力された形そのものに意味がある項目は、元の値を表示用に残し、検索や重複判定にだけ正規化した値を使います。

  • メールアドレス、電話番号、郵便番号: 正規化した値だけ保存してよい
  • 氏名、住所、商品名: 表示用は原文のまま、検索用の列を別に持つ

実装後の確認チェックリスト

  1. 全角数字や半角カナを入れて保存し、半角で検索して見つかるか
  2. 末尾に全角スペースを付けて送信し、前後が除去されるか
  3. 大文字と小文字だけが違うメールアドレスで二重登録が弾かれるか
  4. 正規化の関数が入口の1か所にしかないか(検索で grep して確認)
  5. DB の一意制約は正規化済みの値にかかっているか

Bugoon での実践

入力値の揺れによるバグは、報告者が「何を入力したか」を書き忘れると再現できません。Bugoon のウィジェットをサイトに埋め込むと、スクリーンショットへのアノテーションと操作ステップの記録が報告に付くため、入力した値と画面の状態を開発者が確認できます。GitHub Issue 連携で起票し、MCP サーバー経由で Claude Code や Cursor に修正を任せる流れにもつなげられます。

チームのバグ報告を、もっとスムーズに。

Bugoon は無料で始められます。サイトにタグを 1 行追加するだけで、QA と開発の往復がなくなります。

無料で始める