粘回去的一串为什么半路就停了
转义串粘回这一页的还原框,常见结果不是报错而是短一截。原因在 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 码元。