← 计算与系统基础 · 第 3 篇

同一份数据为什么会被读成不同的值

分清字节、文本与类型,检查 UTF-8、字节序、数字精度和正则完整匹配,给接口补上运行时验证。

学完这一篇,你能做到

为编码、ID 和数值定义转换与校验规则,并拒绝类型和范围错误。

收到一段数据时,程序拿到的可能是字节、文本,也可能是已经解析的对象。它们之间每转换一次,都依赖一份约定:用什么编码、按什么格式解析、字段是什么类型,以及错误怎样处理。

例如商品 ID ITEM-042 应该完整匹配规则,不能因为某个长字符串里包含它就通过校验。金额文本 0.1 也不能在缺少单位和精度约定时,直接被当成适用于结算的二进制浮点数。

字节需要解释规则

字节是八个位组成的单位。相同字节可以解释为整数、字符编码片段、图像数据或协议字段。数据本身通常不能完整说明所有解释方式,因此格式和元数据很重要。

UTF-8 将 Unicode 码点编码为不同数量的字节。汉字“学”的 UTF-8 表示占三个字节,Python 中这个字符串的长度是一个码点。字节数、码点数和用户看到的字符数不能通用地互换。

一个可见字符可能由多个码点组成,例如基础字母加组合标记;某些 emoji 也由多个码点构成。界面截断应考虑字素簇,传输限额则按协议规定的字节或 token 计算。不能简单用字符串长度同时解决这几类问题。

数字的表示也有边界

字节 01 02 按大端解释为 258,按小端解释为 513。网络协议、文件格式和数据库字段应明确字节序与位宽,不能只靠发送端和接收端“恰好一样”。有符号解释还需要约定补码等表示方式。

数字文本与二进制数也是不同类型。字符串 "12" 不会仅因外观看起来是数字,就自动成为所有接口都认可的整数。解析之后应检查范围;标识符里的前导零可能有意义,不能随意转整数再输出。

许多十进制小数无法用有限二进制浮点精确表示。金额可以采用约定币种最小单位的整数,或者适当的十进制表示。要明确舍入方式、币种小数位和计算边界;“全部乘以 100”不是适用于所有币种或业务精度的规则。

类型检查与输入验证各做什么

TypeScript 在编译期检查类型关系,运行时通常不保留这些类型信息。把响应断言为某个接口,只改变类型检查器的看法,不会让接口数据自动符合该结构。

因此,网络边界应先接收不可信数据,再通过运行时校验缩小范围。字段是否存在、是不是要求的类型、长度和数值范围是否允许,分别都要检查。JSON 的结构合法也不代表内容满足业务条件。

Python 里 bool 是 int 的子类。要求严格整数时,isinstance(value, int) 会接受布尔值;是否允许这种行为要由契约决定。类型检查应匹配实际约定,不能只看函数调用是否没有报错。

正则表达式怎样参与校验

正则适合表达受限的文本模式,如固定前缀加三位 ASCII 数字。完整值校验应使用完整匹配语义,避免把“找到一个子串”误当成“整个输入有效”。不同引擎的锚点、转义、Unicode 类别和性能特征可能不同。

Python 的原始字符串减少了语言字符串层的反斜杠转义,但正则引擎自己的转义规则仍存在。这里使用 [0-9],明确要求 ASCII 数字;若换成更宽的数字类别,要确认是否愿意接受其他字符。

不要用一条复杂正则替代所有解析。嵌套结构、日期有效性和跨字段规则通常更适合对应解析器与业务校验。对于可能回溯的表达式,也应限制输入长度,并检查恶意或异常输入上的工作量。

检查同一数据的不同读法

示例验证 UTF-8 长度、字节序和完整 ID 匹配。它的字节串是独立合成输入,代码没有读取文件或调用外部服务。

import re
raw = "学".encode("utf-8")
assert len(raw) == 3 and len(raw.decode("utf-8")) == 1
assert int.from_bytes(b"\x01\x02", "big") == 258
assert int.from_bytes(b"\x01\x02", "little") == 513
assert re.fullmatch(r"ITEM-[0-9]{3}", "ITEM-042")
assert re.fullmatch(r"ITEM-[0-9]{3}", "xITEM-042") is None
print(len(raw), int.from_bytes(b"\x01\x02", "big"))

输出为 3 258。给 ID 加一个前缀后匹配失败,说明校验针对完整输入。这里没有检验真实商品是否存在,也没有证明 ID 可以被当前用户访问,这些属于另外的业务约束与权限检查。

解码错误也需要明确处理。搜索预览可能允许替换无法解码的字符,但关键标识符和签名材料若被静默替换,可能丢失原始信息。应按用途选择拒绝、保留原始字节或显式展示错误,而不是为了“页面看起来正常”统一忽略异常。

在边界保留原始含义

为接口写一份转换表:原始形式、解析规则、目标类型、范围、单位、错误反馈。对金额标明币种与精度,对时间标明时区与格式,对 ID 标明大小写和规范化规则。双方应共享同一契约。

Unicode 规范化可以处理某些等价表示,但不能随意用于所有标识符。规范化、去空格和大小写转换都是语义决定;若改变输入,应明确记录和定义,而不是在不同模块里各自悄悄处理。

练习:验证一个商品记录

设计一个接收字节形式 JSON 的校验器,明确只接受 UTF-8、完整商品 ID、非负的整数最小单位金额和已支持的币种。先解码,再解析,再校验字段,最后执行业务与权限检查。

验收时加入非法 UTF-8、缺失字段、金额是字符串或布尔值、ID 带多余前缀、金额超出上限和未知币种。每类失败都应得到稳定的错误类别。保留可定位问题的元信息,日志不要记录真实账号或支付数据。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。