貼回去的一串為什麼半路就停了
轉義串貼回這一頁的還原框,常見結果不是報錯而是短一截。原因在 fromUnicode(L126–150):四枚正則排隊各掃一遍整串,位置在 L130 / L135 / L140 / L145——步1 認 \u 加 {4,6} 位十六進位,步2 認 &#(\d+);,步3 認 &#x([0-9a-fA-F]+);,步4 認裸反斜線加十六進位位、末尾再掛一枚 \s?。每步都重掃整串,後三步讀的就是前三步剛吐出的字元。中間態由純 Node + 假 DOM harness 跑出:四步表示式從模板逐字截出執行,與頁內函數在 96 條輸入上逐條相同;渲染層未實測。
接力最少 2 跳:\u0026#65;(10)步1 收成 A(5)、步2 收成 A;\0041(9)要等步4 才收尾;\u0026#x5c;0041(15)走滿 3 跳,且必須靠步3 排在步4 之前。擴樣 39 條鏈式語料:步1 有變化 22 條,第一跳後仍可被後面三步匹配 30 條,第二跳後 22 條,第三跳後 13 條。
圖 1:四個方框標步序與命中數
模式排在哪一步,就歸哪一步管
- 停在 5 碼元:
A(10)只有步3 吐得出A,可步2 已經跑過 - 停在 5 碼元:
\u0026#38;#65;(14)步2 之後落在這裡,沒有第五步來救 - 停在 6 碼元:
\u0041(10)吐出的\u0041夠不著步4,它要求反斜線緊跟十六進位位而u不算
貪婪的 {4,6}:漢字後面跟一個 A 就壞一串
編碼側定長 4 位十六進位、轉義串裡不留分隔符,還原側上限卻是 6 位。緊跟轉義的字元只要是十六進位字母就被當成第 5 位吃掉:中A 編成 \u4e2dA(7),只剩 1 碼元的 U+E2DA,賬目是 20013 併成 320218、取餘 58074。同形下 工F 走 24037 → 384607 → 56927,中文A(13)回成中 + U+587A;反例是不算十六進位的 G,中G 原樣回 2 碼元。
1194 條合成語料(6 枚漢字 × 65 枚 ASCII × 3 種位置,另加 24 條代理對組合)跑 6 個檔位共 7164 次往返,hex/non-ascii 透過 798/1194 = 66.83%,失敗全落在緊鄰字元屬於 0-9a-fA-F 那一組:hexLetter 0/216、digit 0/180,合起來 0/396;其餘 774 條 774/774 全過。語料按類目合成,不能讀成「真實文本裡有多少比例會壞」。對照檔位見邊界:scope=all 下每碼元定長,中A 變 \u4e2d\u0041(12)就能原樣回來,hex 與 hex-css 各 1194/1194 = 100.00%;dec 檔靠分號定界,兩種 scope 也各 100.00%。hex/non-ascii 與 hex/all 的差值正是那 396 條。
超出四位的寫法靜默截斷
超界碼位不給回饋,按 65536 取餘了事:\u1F600(7)、😀(9)、😀(9)三條都給同一個 U+F600,餘數 62976;\u1FFFFF(8)落在 U+FFFF,�(15)也只吐 1 碼元。String.fromCharCode 的取模另測 8 個點,含 128512→62976、1000000→16960、2147483648→0。寫法只影響能不能回來:拆成兩段的 \uD83D\uDE00(12)回到 2 碼元,只寫半截的 \uD83D(6)留下孤立代理。96 條輸入 0 次拋異常,fromUnicode 沒有 try/catch 也沒有 toast。
CSS 那一檔:一枚空格、一組短轉義
步4 末尾的 \s? 只吞 1 個空白,這一檔另有一種損失:\4e2d \6587(11)回成 2 碼元、空格沒了,\4e2d \6587(12)還剩 3 碼元,製表符與全形空格同樣各被吞 1 個;本頁自產的串也照吃,中文 工具 轉成 21 碼元串、回來只剩 4 碼元。hex-css/non-ascii 因此降到 744/1194 = 62.31%,punct 類只剩 180/234。反向差異來自恆 4 位輸出:padStart(4,'0') 讓 128–65535 全 65408 個碼位恆 5 碼元,別處產的短轉義夠不著 {4,6} 下限——\41 \42(7)與 \4e(3)原樣留著,挨著的兩枚兩位轉義 \4142(5)反被併成 1 枚 4 位、還原成 U+4142。
交換按鈕不是撤銷
swapText(L194–208)在輸出框 0 碼元時只發 1 次「無可交換的內容」就 return,輸入、mode、結果卡、統計、select 五處不動。有內容時它順手切模式:to 檔輸入 中文工具 得 24 碼元,交換後 mode 變 from、統計變 24 / 4,再換一次回到初值——連按兩次回得來,但中間那次已改了模式。被吞掉的那條換不回:from 檔輸入 \u4e2dA(7)得 1 碼元的私用區碼位,切回 to 檔又被當成新漢字轉成 \ue2da(6)。兩枚 select 的值不被清空(L93/L94 只寫父元素 display),clearAll 清四處也不清 mode 與 select。隱藏效果、toast 時長、placeholder 是否截斷均未實測。
對照列表頁文案
config/tools.php 第 997–1011 行的 intro 逐條判了 16 條:一致 7、衝突 2、部分一致 7。兩條衝突都在排他表述上:把還原說成只認 \uXXXX(A(6)與 \4e2d(5)也解),以及小貼士「4位十六進位」對上 {4,6}。結論只到這一頁。概念層在中文 Unicode 互轉舊文已寫過、本篇不復述;檔位與選項看 unicode-convert 工具頁,換格式族看 文字編碼頁。
FAQ
Q:為什麼中文後面跟個 A 就壞了?
還原側上限 6 位、轉義串又不留分隔符,A 屬十六進位字母就被併成第 5 位,兩個字元換成 1 碼元;換成不算十六進位的 G 就能原樣回來。
Q:為什麼它既不報錯也不提示?
字串層沒有失敗分支:parseInt 拿到幾位算幾位,超界按 65536 取餘,殘缺寫法原樣留著,96 條探針輸入 0 次拋異常。
Q:怎麼寫才能原樣回來?
在本頁檔位內,轉義之間有邊界就能過——scope=all 與 dec 各 1194/1194 = 100.00%;超四位的碼位要拆成兩段寫,\uD83D\uDE00(12)回 2 碼元,寫成 \u1F600(7)只剩 1 碼元。