文字與正規表示式
字數沒超過,為何超出位元組限制?
使用工具
輸入框寫著「最多 20 個字元」,看起來很短卻報錯;也可能輸入時通過,儲存時才被拒絕。使用者不一定數錯了:可見字元、程式中的單位與儲存單位並不總是相同。
用字元與位元組計數工具,讓同一輸入接受幾種測量。它只描述所輸入的字串,不會連線到出錯服務或讀取資料庫設定。把它當作縮小原因範圍、再與實際規格和實作核對的小實驗。
分別輸入 ABC、あいう 和 😀
先只輸入 ABC,字素、碼位、UTF-16 單元和 UTF-8 位元組都是 3。換成 あいう,前三項仍是 3,UTF-8 位元組卻為 9。這說明為什麼只測試 ASCII 可能漏掉日文很快就會觸發的位元組上限。
input | graphemes | codePoints | utf16CodeUnits | utf8Bytes
ABC | 3 | 3 | 3 | 3
あいう | 3 | 3 | 3 | 9
😀 | 1 | 1 | 2 | 4再只輸入 😀:一個字素、一個碼位,卻是兩個 UTF-16 單元、四個 UTF-8 位元組。所以「表情算兩個字元」並不完整,必須說明用哪種計數。由多個元素組成的表情,數字還可能不同。
外觀相同的 é,內部不一定相同
比較預組合的 é 與 e 後接組合銳音符。前者一個碼位、兩個 UTF-8 位元組;後者兩個碼位、三個 UTF-8 位元組。通常的字素分割都會把它們當作一個可感知字元。
é
é兩行一起貼上會把換行也計入,所以要分別試。Unicode 資訊工具可顯示 U+00E9 與 U+0065、U+0301。正規化可能統一它們,但不能為了湊長度就無提示地改寫輸入。涉及識別碼或簽章時,應先確認接收方規則。
12 位元組的欄位能放什麼?
假設欄位限制為 12 個 UTF-8 位元組。12 個 A、4 個 あ、3 個 😀 都恰好達到邊界。各增加一個後分別為 13、15、16 位元組。測試邊界前、邊界上和邊界後,比隨便貼上長文更容易解釋問題。
不能據此把位元組數除以三當作通用字元上限。拉丁字母、漢字、表情和組合序列混合時,每字元的位元組數並不固定。產品可以分別說明並驗證面向使用者的字元限制和儲存限制;如何選擇是產品需求,不由計數器自動決定。
空白與最後的換行也是輸入
ABC 後面加一個普通空白,UTF-8 就變成 4 位元組。視覺變化很小,看似相同的貼上文字也可能計數不同。用行整理去空白是在修改輸入。排查限制時先測原文,再記錄哪一步使它變短。
這裡按換行符號計行:A 後跟一個換行,包含最後空行,共兩行。窄螢幕上的自動折行不會增加輸入換行。LF 與 CRLF 的儲存位元組也不同;貼到瀏覽器可能統一換行,因此不能把這裡的數直接視為原檔案大小。
字串位元組數不等於完整傳輸大小
12 位元組的名字放進 JSON 後,還會增加鍵名、引號和分隔符號。URL 編碼後的表示長度也不同。檔案還可能涉及 BOM、換行和壓縮。檢查傳輸限制時,要區分原字串與實際序列化資料的大小。
字素計數使用瀏覽器的 Intl.Segmenter,不支援時不會填入猜測值。分割還取決於執行環境的 Unicode 資料,不能保證與另一個服務完全一致。若僅特定裝置出錯,應在相同輸入下記錄裝置與瀏覽器。
把「很短卻報錯」變成可重現報告
記錄範例、字素數、UTF-16 單元、UTF-8 位元組以及在哪一步被拒絕。無需使用真實姓名或秘密文字,同類型字元的短合成範例更便於分享。「三個 😀 通過,四個在儲存時失敗」比「表情不能用」更能縮小條件。
知道數字後,就能依次核對頁面計數、輸入驗證、API 限制與儲存定義。不要只擴大上限,先確認它們應在哪種單位上保持一致。計數器不能修復系統,但能幫助建立一致約定和使用者看得懂的錯誤說明。
測試恰好達到上限與剛超出的輸入
UTF-8 上限 12byte 時,あいうえ 為 12byte,あいうえお 為 15byte;ABCDEFGHIJKL 也為 12byte,視覺長度卻不同。測試兩者恰好通過、再加字元被拒,可區分字元數與位元組限制。ABCDEFGHIJK😀 是 11 加 4 共 15byte,並非已有十一英文字母就能再放任意一個字元。
按任意位元組位置截斷可能切開 UTF-8 字元,按碼位切也可能拆開組合字元或表情。計數器量測長度,不自動安全縮短文字。輸入系統應明確是提示上限讓使用者修改,還是按哪種單位截斷,並確認顯示計數與實際限制使用同一單位。