在處理中文、日文與韓文(CJK)多語系網站時,除了透過 JS 英文數字全形轉半形:charCodeAt 字元碼位移與全形空白轉換實作 處理純英數外,ES6 原生提供的
String.prototype.normalize方法也能直接透過 Unicode 標準化形式(NFKC)一行完成寬度轉換。本文將比較normalize('NFKC')與字元碼位移的差異,並提供「半形轉全形」的反向轉換寫法。
一、 全形與半形字元轉換方法比較表
在電腦編碼與排版中,全形字元(Full-width characters)在等寬字體下佔用兩個標準字元寬度,而半形字元(Half-width characters)則佔用一個字元寬度。針對不同的轉換範圍,可選用以下方法:
| 轉換需求與方法 | 使用語法 | 轉換涵蓋範圍 | 注意事項與副作用 |
|---|---|---|---|
| Unicode 相容正規化(全形轉半形) | str.normalize('NFKC') | 全形英數、全形空白、半形日文片假名、圈號數字(①)與單位符號(㎡) | 會一併把 ① 轉成 1、上標 ² 轉成 2 |
| ASCII 字元碼位移(全形轉半形) | charCodeAt(0) - 0xFEE0 | 僅限 \uFF01~\uFF5E 全形英數與標點 | 不會誤動特殊符號(詳見 JS 英文數字全形轉半形) |
| ASCII 字元碼位移(半形轉全形) | charCodeAt(0) + 0xFEE0 | \u0021~\u007E 半形英數與半形空白 | 適用於日本郵政地址或金融系統規定全形欄位 |
二、 使用 String.prototype.normalize('NFKC') 進行 Unicode 正規化
ECMAScript 6 引入的 String.prototype.normalize([form]) 支援 4 種 Unicode 正規化模式:NFC(預設)、NFD、NFKC 與 NFKD。其中帶有 K(Compatibility,相容性分解) 的 NFKC,會自動將「全形相容字元」與「全形空白 \u3000」轉換為標準的半形字元:
function convertToHalfWidthByNFKC(str) {
if (typeof str !== 'string') return '';
return str.normalize('NFKC');
}
// 範例 1:全形英數字、符號與全形空白一次轉為半形
const fullWidthStr = 'abcdefg+! 123';
console.log(convertToHalfWidthByNFKC(fullWidthStr));
// 輸出: "abcdefg+! 123"
// 範例 2:連同圈號數字與合字單位也會一併正規化拆解
const specialStr = '項目①、面積 100㎡、電話 0912';
console.log(convertToHalfWidthByNFKC(specialStr));
// 輸出: "項目1、面積 100m2、電話 0912"
⚠️ 使用 normalize('NFKC') 的注意事項
由於 NFKC 的設計目標是「消除所有視覺變體以利搜尋比對」,它除了把全形英數轉為半形外,還會把以下字元拆解:
- 帶圈數字
①、②會變成普通數字1、2。 - 平方符號
²、³會變成普通數字2、3。 - 羅馬數字字元
Ⅳ會變成兩個英文字母IV。
若您的表單需要保留 ① 或 ² 等特殊符號的原貌,僅想轉換英數字,請改用 JS 英文數字全形轉半形 的 charCodeAt(0) - 0xFEE0 寫法。
三、 反向需求:JavaScript 半形轉全形實作
在某些銀行電文、政府公文系統或日文地址表單中,後端資料庫反而會強制要求將所有半形英數字(\u0021~\u007E)與半形空白(\u0020)統一轉為全形字元。此時無法使用 normalize(),而是將字元碼加上 0xFEE0(65248):
/**
* 將半形英數字、標點符號與半形空白轉換為全形
* @param {string} str - 原始字串
* @returns {string} 轉換為全形後的字串
*/
function convertHalfToFullWidth(str) {
if (typeof str !== 'string') return '';
return str
.replace(/[\u0021-\u007e]/g, (char) => {
return String.fromCharCode(char.charCodeAt(0) + 0xfee0);
})
.replace(/\u0020/g, '\u3000');
}
const halfStr = 'ABC 123!@#';
console.log(convertHalfToFullWidth(halfStr));
// 輸出: "ABC 123!@#"
若需要透過碼點處理超出基本多文種平面(BMP)的特殊字元,亦可搭配 JavaScript 使用 String.fromCodePoint() 將 Unicode 轉字符 操作。
四、 常見問題與排錯 Q&A
Q1:使用者在中文輸入法(IME)拼字選字過程中觸發 normalize 會發生什麼事?
- 問題原因:在 React 或原生
<input>的onChange/input事件中,若在使用者還在打注音符號尚未按下 Enter 選字(isComposing === true)時就強制執行全形轉半形替換,會導致注音輸入中斷或重複拼音。 - 解決思路:請在
onBlur(失去焦點)時才執行轉換,或搭配compositionstart/compositionend事件確認選字完成後再處理。
Q2:為什麼搜尋框實作站內關鍵字比對時推薦使用 normalize('NFKC')?
- 問題原因:使用者在搜尋產品型號時,有人會輸入全形
IPhone 16,有人會輸入半形iPhone 16。 - 解決思路:在比對前先對搜尋詞與目標文字同時執行
str.normalize('NFKC').toLowerCase().trim()(空白處理可參考 JavaScript 去除字串前後空白、空格與換行),即可讓全半形輸入都命中同一筆資料。
發表迴響