紀錄工作經驗、相關知識,解決技術相關問題。

Javascript, React, 工作雜談, 新手 Jamp Holes, 網站相關, 資訊相關

JS 英文數字全形轉半形 (charCodeAt 與正規表達式寫法)

JS 英文數字全形轉半形 (charCodeAt 與正規表達式寫法) 特色圖片

在前端表單填寫時,常會遇到後端 API 或驗證規則限定只接受半形字元,但使用者輸入時不小心切換到全形輸入法,導致驗證碼、電話或金額數字變成全形(如 ABC123)而無法通過 Input RegExp 表單驗證 — 只輸入數字。透過 JavaScript 的 charCodeAt 字元碼位移,即可在前端即時完成全形轉半形處理。

(註:若您需要處理日文片假名、特殊相容符號或「半形轉全形」雙向轉換,請參閱另一篇 JavaScript Unicode 字元寬度轉換:String.prototype.normalize NFKC 與半形全形雙向轉換。)


一、 核心複製即用範本:全形英數字與全形空白轉半形

以下函式可同時處理所有可見的全形 ASCII 符號、英文字母、數字(Unicode 範圍 \uFF01 ~ \uFF5E)以及全形空白(\u3000):

/**
 * 將字串中的全形英數字、標點符號與全形空白轉換為半形
 * @param {string} str - 原始輸入字串
 * @returns {string} 轉換為半形後的字串
 */
const fullShapeToHalfShape = (str) => {
  if (typeof str !== 'string') return '';
  return str
    .replace(/[\uff01-\uff5e]/g, (ch) => {
      return String.fromCharCode(ch.charCodeAt(0) - 0xfee0);
    })
    .replace(/\u3000/g, ' ');
};

const inputStr = 'ABCDEFG 123!@#';
console.log(fullShapeToHalfShape(inputStr));
// 輸出: "ABCDEFG 123!@#"

二、 轉換原理:為什麼是減去 0xFEE0(65248)?

在 Unicode 編碼表中,半形可見 ASCII 字元與對應的全形字元之間,存在固定的線性位移差距 65248(十六進位為 0xFEE0),唯一的例外是「空白鍵」:

字元類型半形 Unicode 編碼(十進位 / 十六進位)全形 Unicode 編碼(十進位 / 十六進位)兩者編碼差值
感嘆號(! / !)33 (0x0021)65281 (0xFF01)相差 65248 (0xFEE0)
數字 0(0 / 0)48 (0x0030)65296 (0xFF10)相差 65248 (0xFEE0)
大寫 A(A / A)65 (0x0041)65313 (0xFF21)相差 65248 (0xFEE0)
波浪號(~ / ~)126 (0x007E)65374 (0xFF5E)相差 65248 (0xFEE0)
空白(Space)32 (0x0020)12288 (0x3000)例外(需單獨替換)

因此,只要用正規表達式 /[\uff01-\uff5e]/g 抓出落在此區間的全形字元,將其 charCodeAt(0) 減去 0xFEE0,再搭配 .replace(/\u3000/g, ' ') 將全形空白替換為半形空白即可(若要直接刪除空白,可搭配 JavaScript 去除字串前後空白、空格與換行)。


三、 僅轉換「全形數字」的精準寫法

如果在特定欄位中,您只希望把使用者輸入的「全形數字 0~9」轉成半形 0~9,而不希望動到中文全形標點符號(例如保留中文全形逗號 ,、問號 ? 與驚嘆號 !),可以將正規表達式範圍縮小至 /[\uff10-\uff19]/g:

/**
 * 僅將全形數字(0-9)轉為半形數字(0-9),保留中文全形標點
 */
const fullNumberToHalf = (str) => {
  return String(str).replace(/[\uff10-\uff19]/g, (ch) =>
    String.fromCharCode(ch.charCodeAt(0) - 0xfee0)
  );
};

console.log(fullNumberToHalf('訂單編號:12345,請確認!'));
// 輸出: "訂單編號:12345,請確認!"(全形冒號、逗號與驚嘆號皆維持原狀)

參考資料與延伸閱讀

發表迴響