你是否曾把表單送出後,系統卻回應「格式錯誤」,但明明目測完全正確?或是在 Excel 排序時,同樣的數字卻排在不一起的地方?這些惱人問題,往往根源於一個看似微小的差異——全形(全角)與半形(半角)字元的混用。本文將完整解析兩者的定義、差異及轉換時機。
1. 全形與半形的定義
在東亞字元集(尤其是 CJK 環境)中,字元依照顯示寬度分為兩大類:
| 類型 | 英文 | 寬度 | 典型範圍(Unicode) | 範例 |
|---|---|---|---|---|
| 半形 | Half-width | 1 個字元寬 | U+0021–U+007E(ASCII 可見字元) | A B C 1 2 ! @ # |
| 全形 | Full-width | 2 個字元寬 | U+FF01–U+FF60(全形 ASCII 對應) | A B C 1 2 ! @ # |
簡單說:全形字元在等寬字型下佔兩個半形的寬度,設計上讓英數字母能與漢字「等寬對齊」;半形字元則與一般 ASCII 標準相符,是現代電腦程式、URL、程式碼所用的標準字元集。
漢字、平假名、片假名本身就屬於全形範疇,不存在「半形漢字」。本文討論的全形/半形轉換,主要針對英文字母、數字、標點符號這三類存在雙版本的字元。
2. 全形與半形的具體差異
以下對照幾個最常見的字元類別:
| 類別 | 半形 | 全形 |
|---|---|---|
| 大寫英文 | A B C Z | A B C Z |
| 小寫英文 | a b c z | a b c z |
| 數字 | 0 1 2 9 | 0 1 2 9 |
| 常用標點 | ! @ # $ % ( ) , | ! @ # $ % ( ) , |
| 空白 | (普通空格 U+0020) | (表意文字空格 U+3000) |
這些字元在外觀上「看起來一樣」,但它們的 Unicode 碼點完全不同,電腦在比較、搜尋、排序時會視為完全不同的字元。這正是混用時出問題的根本原因。
3. 為什麼會出現混用?
全形字元的出現有其歷史脈絡。早期的 CJK 字元集(如 Big5、Shift-JIS)為了讓英數標點能與漢字混排時整齊對齊,在編碼中收錄了全形版本的 ASCII 字元。輸入法(尤其是日語 IME、繁體中文輸入法)通常提供「全角模式」切換,讓使用者在同一輸入模式下打出全形英數。
常見的混用來源:
- 輸入法在全形模式下輸入的數字、標點(例如在中文輸入模式下打出的逗號
,是全形,英文逗號,是半形)。 - 從 Word、PDF、掃描件複製貼上的文字。
- 使用者對全形半形模式不自知(手機鍵盤、日文 IME 尤其常見)。
- 不同系統之間的資料交換,原始資料已含有全形字元。
4. 什麼時候需要轉換?
以下幾個場景最容易踩坑,也是最需要轉換的時機:
4.1 表單驗證與資料庫儲存
使用者填寫電話號碼 02-2300-1234(全形數字+連字號),正規表達式 /^\d{2}-\d{4}-\d{4}$/ 完全無法匹配,因為全形數字不屬於 \d。解法:在後端接收資料後,先執行全形→半形正規化,再進行格式驗證。
4.2 搜尋與比對
資料庫中存著半形 iPhone,使用者卻搜尋全形 iPhone,結果一筆也找不到。若搜尋引擎未做字元正規化,這類問題需在應用層統一轉換。
4.3 CSV / Excel 資料處理
數字欄位夾雜全形,讓 Excel 的 SUM() 把那格視為文字而跳過;或排序時全形數字 1 排在 9 後面,而非數值正確位置。
4.4 程式碼與設定檔
JSON、YAML、.env 等設定檔若混入全形引號 " 或全形冒號 :,解析器會直接報錯。這種錯誤有時肉眼極難察覺。
4.5 URL 與 Email 地址
全形字元不能直接出現在 URL 或 Email 地址中。例如 user@example.com 中有全形 @(U+FF20),郵件系統通常無法辨識。
4.6 排版對齊
反過來,全形→半形並非萬能方向。在某些傳統排版場景(如印刷、台灣政府公文格式)中,標點符號要求使用全形,半形標點反而被視為格式錯誤。此時需要半形→全形的轉換。
5. 轉換規則與注意事項
全形 ASCII 字元(U+FF01–U+FF5E)與半形(U+0021–U+007E)之間的轉換規則非常簡單:
// 全形 → 半形(以 JavaScript 為例)
function toHalfWidth(str) {
return str.replace(/[\uFF01-\uFF5E]/g, ch =>
String.fromCharCode(ch.charCodeAt(0) - 0xFEE0)
).replace(/\u3000/g, ' '); // 全形空格 → 半形空格
}
// 半形 → 全形
function toFullWidth(str) {
return str.replace(/[\u0021-\u007E]/g, ch =>
String.fromCharCode(ch.charCodeAt(0) + 0xFEE0)
).replace(/ /g, '\u3000'); // 半形空格 → 全形空格
}
轉換時有幾個細節值得注意:
- 漢字與假名不受影響:轉換函式只需針對 Fullwidth ASCII(U+FF01–U+FF5E)與 ASCII(U+0021–U+007E)的對應區段操作,漢字完全不在此範圍。
- 全形空格(U+3000,表意文字空格)需單獨處理,因為它不在 U+FF01–U+FF5E 的區段內。
- 片假名的半形版本(U+FF65–U+FF9F)是另一獨立區段,與全形 ASCII 的轉換邏輯不同,需另外處理。
- 不要盲目雙向轉換:先確認目標系統期望接收的是全形還是半形,再決定轉換方向。
6. 各語言/平台內建支援
| 語言 / 平台 | 全形→半形 | 說明 |
|---|---|---|
| PHP | mb_convert_kana($str, 'a') | 'a' 轉全形字母→半形,'A' 反向;需 mbstring 擴充 |
| Python | unicodedata + str.translate | 標準庫無直接函式,需自行實作對應表或使用 jaconv 套件 |
| JavaScript | 正規表達式(見上方) | 無內建函式,需自行實作 |
| Java | 自行實作或 Apache Commons | 無標準函式庫直接支援 |
| MySQL | 無內建 | 需在應用層處理後再存入 |
| Excel | ASC() / JIS() | ASC() 全形→半形,JIS() 半形→全形(限日文環境) |
7. 實戰建議:資料收集時的正規化策略
與其每次用到資料才轉換,不如在資料進入系統的入口統一正規化:
- 後端收到表單資料後立即正規化:電話、郵遞區號、信用卡號等格式敏感欄位,在驗證前先轉半形。
- 搜尋查詢也要正規化:搜尋關鍵字與被搜尋資料使用同一字元標準,確保命中率。
- 匯入 CSV 時做前置清洗:使用腳本在匯入資料庫前掃描並轉換帶有全形字元的欄位。
- 在設定檔 Lint 中加入全形字元偵測:利用 CI/CD 的 pre-commit hook 或 linter 在上線前攔截意外混入的全形字元。
8. 小結
全形與半形的差異雖小,但在資料驗證、搜尋、程式開發與排版等場景中都可能造成不可忽視的問題。核心原則很簡單:
- 程式邏輯、URL、API、資料庫索引欄位 → 優先使用半形。
- 傳統中日韓排版、政府公文、印刷稿 → 依規範使用全形標點。
- 在系統邊界做一次性正規化,而非到處修補。
遇到需要批次轉換的場合,可以使用本站的文字轉換工具,快速完成全形↔半形互轉,省去手動處理的時間。