もう二度と数値入力で消耗したくない人のための『数値正規化』完全ガイド

数値入力フォームは、一見シンプルに見えて落とし穴が多い。

  • 全角数字が混ざる
  • 通貨記号やカンマが入る
  • -. が想定外の場所に出る
  • 空欄や入力途中の状態の扱いがややこしい

ここでは 「数値正規化」 に絞って、Vueでもそのまま使える例をまとめる。

目次

数値入力の処理は3段階

数値入力を次の3段階に分けると整理しやすい。

① 正規化レイヤー:文字/数値の正規化

② 修正処理レイヤー:少数桁制限や丸めなどの修正処理

③ バリデーション:確定した値に対して制約を検証

少数桁数制限や丸め、最小値・最大値チェックなどは ②修正処理レイヤーで処理する
正規化レイヤーの目的は、壊れにくい形に揃えること

修正処理レイヤーでは、

  • 空欄はデフォルトで null
  • 0 として扱うかどうかはオプションで制御

といった 入力値の最終的な意味付けを行う。
これにより、正規化段階では入力途中・未入力の状態を保持できる。

数値正規化とは

数値正規化は、同じ数値を別の表記で書いたものを同じ形の文字列に揃える処理。
数値正規化後のデータは、null または数値として処理できる形式の文字列とする。

正規化でやること

  • 全角 → 半角
  • 記号の表記揺れを統一
  • 不要文字の削除
  • .- の位置整理
  • 未入力状態の統一(null

正規化でやらないこと

  • 少数桁数制限
  • 丸め(切り捨て/四捨五入)
  • 数値の意味を変える補正

基本方針

  • 数値として解釈しない
  • 情報量は減らさない
  • 判断は後段に任せる
  • 入力途中でも壊れない

バリデーション前の前処理レイヤーとして考える。

許可する文字

残す文字は0–9, -, . だけ。それ以外は削除対象とする。

正規化の手順

  1. マイナス記号の統一
    • 見た目が似ている Unicode のマイナスをすべて - に置き換える。
    • 主な対象文字:
      −(U+2212, 数学記号のマイナス)
      ―(U+2015, 水平線)
      ー(U+30FC, 長音符)
      –(U+2013, エンダッシュ)
      —(U+2014, エムダッシュ)
      ➖(U+2796, Heavy Minus Sign)
      ﹣(U+FE63, Small Minus Sign)
      -(U+FF0D, 全角ハイフン)
      ﹘(U+FE58, Small Em Dash)
  2. 許可文字以外を削除
    • 対象文字以外はすべて削除
  3. 許可文字以外を削除
    • 対象文字以外はすべて削除
  4. 小数点整理
    • 最初の1つだけ残す
    • 2つ目以降は削除
  5. マイナス整理
    • 先頭の1つだけ残す
    • それ以外は削除
  6. 0補完
    • . で始まる場合 → 0. を付与
    • -. で始まる場合 → -0. を付与
  7. 空の扱い
    • 空欄や入力途中は null

実装例(Vue対応)

function normalizeNumber(input) {
    let s = input ?? "";

    // 0. 全角 → 半角(数字・記号)
    s = s.replace(/[0-9]/g, (c) =>
        String.fromCharCode(c.charCodeAt(0) - 0xfee0)
    );
    s = s.replace(/./g, ".");

    // 1. マイナス記号の統一
    s = s.replace(/[−―ー–—➖﹣-﹘]/g, "-");

    // 2. 許可文字以外を削除
    s = s.replace(/[^0-9.\-]/g, "");

    // 3. 小数点は最初の1つだけ
    const dot = s.indexOf(".");
    if (dot !== -1) {
        s = s.slice(0, dot + 1) + s.slice(dot + 1).replace(/\./g, "");
    }

    // 4. マイナスは先頭1つだけ
    s = s.replace(/(?!^)-/g, "");

    // 5. 0補完(オプション)
    if (s.startsWith("-.")) s = "-0." + s.slice(2);
    else if (s.startsWith(".")) s = "0" + s;

    // 6. 空の扱い
    if (!/[0-9]/.test(s)) return null;

    return s;
}
  • 返り値は string | null
  • 入力途中でも破綻しない
  • Vue / plain JS 両対応

入力例と正規化結果

入力正規化後
¥ 1,230 –1230
123-456123456
12.3.412.34
–123-123
.-1230.123
-.123-0.123
-1 234.5-1234.5
0.001230.00123
.50.5
-.5-0.5
abcnull
null
.0.
-.-0.
空白null

0.-0. の入力は、正規化処理の中で自動的に 0. / -0. に補完される。

これらを 0null にするかどうかは、入力確定(blur / submit)が必要になるため、修正処理レイヤーに任せる。

Vueでの使いどころ

  • v-model に入れる前
  • watch / computed 内
  • バリデーションライブラリの前段

null で返すことで、未入力・入力途中・確定済みを分けて扱える。

まとめ

  • 数値入力はレイヤー分離が便利
  • 数値正規化は表記揺れの整理
  • 数値の意味を変える処理は修正処理レイヤーで行う
  • 空欄の扱いはnull

コメント

コメントする

目次