SheepDev

文本与正则表达式

字数没超,为什么超过字节限制?

使用工具

输入框写着“最多 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 字符,按码点切也可能拆开组合字符或表情。计数器测量长度,不自动安全缩短文字。输入系统应明确是提示上限让用户修改,还是按哪种单位截断,并确认显示计数与实际限制使用同一单位。

参考资料

使用工具

指南 →