Python 04:字符串怎样变成可靠输入
"你好 世界" 是五个 Python 字符,却编码成十三个 UTF-8 字节。如果限制的是接收端的字节数,却用 len(text) 检查,边界会放错位置;如果把错误字节悄悄替换成别的字符,词频结果即使“成功”,也不再对应原始输入。
tasklab 现在增加 submit_bytes,接收生成的 UTF-8 数据,仍转入同一个 submit → run → query 业务路径。前一章处理任务元数据的复制边界;这里处理数据进入应用前的编码与大小边界。
字节数不是字符数
Python 的 str 是文本序列,bytes 是字节序列;二者在应用边界处需要明确转换。官方 Unicode HOWTO解释了编码、解码以及错误策略。本实验固定 UTF-8;不会从一串任意字节“猜”原本的编码。
对实际输入 "你好 世界",len(text) 为 5,len(text.encode("utf-8")) 为 13。submit_bytes 先要求输入是 bytes,再检查原始字节长度是否超过 1024,随后调用 raw_bytes.decode("utf-8", errors="strict")。submit 在接收已有的 str 时也检查编码后的长度,避免从字符串入口绕过同一限额。两种入口最终都保存字符串,词频执行规则没有分叉。
这一顺序还能定位失败所属的层。b"\xff" 长度未超限,但不是合法 UTF-8,严格解码会抛 UnicodeDecodeError;把 "汉" 重复 342 次编码后是 1026 字节,首先命中上限,抛 ValueError。长度检查先于解码,因此又长又坏的字节会先报告大小错误;这不是“错误输入被接受”,而是应用明确规定的失败优先级。
所有校验发生在任务 ID 生成之前。非法字节、超限字节或超限 str 不增加任务,也不更改已提交任务;下一次合法提交得到连续的 task-0002。这条不变量与 00 的非法任务类型、03 的非法元数据相同,不能每增加一种入口就降低拒绝输入的标准。
数字转换是另一个边界
若以后需要从文本中读取大小或重试次数,int("12") 与 int("12x") 是两次不同的转换:前者返回整数 12,后者抛 ValueError;具体转换规则见 Python 3.12 的 int() 文档。字节解码成功只证明文本编码合法,不能证明它代表合法整数。词频任务当前不解析用户提供的数字,实验仅演示这第二层失败;没有把异常转换成任务运行失败或成功结果。
有些解码代码会使用 errors="replace",对无法解释的字节插入替代字符。对于搜索预览这种策略可能是显式需求;本任务若这么做,就必须承认处理的已不是原字节所表达的文本,并加入输出差异断言。当前选择严格拒绝而不静默更改输入。
可重放的正反例
在 examples/python-language/ 目录执行:
1 | |
实验输出记录 "characters": 5、"utf8_bytes": 13、"result": {"你好": 1, "世界": 1}、拒绝类型 UnicodeDecodeError 和 ValueError,还记录 "numeric_error": "ValueError"。两条命令退出码都为 0,第二条累计执行 15 条测试;非法输入后任务数仍为 1,下一个合法 ID 为 task-0002。环境、真实命令、原始输出和断言保存在 examples/python-language/evidence/04/20261007-utf8/。这里只验证 UTF-8 和本地内存限制,并未测服务端请求大小或网络半包;那些边界属于 34。
预测题
把 "汉" * 342 发给 submit_bytes,如果误用字符数检查,为何可能放过超限请求?把重复次数改成 341,在本次字节限制下应怎样?
答案:342 个汉字少于 1024 个字符,却占 1026 个 UTF-8 字节,所以字符数检查会放过本应拒绝的输入。341 个相同汉字占 1023 字节,按当前长度检查应可进入提交路径;后一变式未纳入本轮原始运行记录,若把它作为产品边界测试,还须补一条真实用例。
变更题
若把 errors="strict" 改成 errors="replace",仅保留“调用没有抛异常”的断言,足以判定词频结果正确吗?
答案:不足。需要保存原始字节、解码后文本和任务结果,明确拒绝非法输入还是容许替代字符,并比较与原来受控输入的差异。当前代码没有引入替换策略,非法 b"\xff" 的拒绝路径已经由 test_invalid_utf8_and_oversized_input_do_not_change_store 实测。
下一章讨论函数签名与默认值绑定:输入校验写对了,默认参数仍可能让两次提交意外共享同一个容器。系列导航仅链接实际正文。




