数字解码的危险发生在累计过程中

一段文本可能完全由合法数字组成,却仍然超出目标数据类型的范围。只验证字符集合,会把容量问题留给后续算术,最终得到无法解释的结果。

因此,解码需要同时维护两条边界:当前字符在目标进制中是否合法,以及把它加入已有结果之后是否仍然可表示。两者都通过,才继续推进。

常见解码写法是:当前值乘以基数,再加当前位的数值。如果等循环结束后才检查结果是否超过上限,某一步运算可能已经溢出,原来的数值关系就不再可靠。

正确的检查位置应在每次乘加之前。把“下一步必须不超过上限”改写为当前值和下一位之间的比较,就能在不执行危险运算的前提下拒绝输入。

把乘加不等式拆成商与余数

示例限制到无符号 32 位范围Python
text = "4294967295"
base, limit, value = 10, (1 << 32) - 1, 0
for char in text:
    if not "0" <= char <= "9":
        raise ValueError("非法数字")
    digit = ord(char) - ord("0")
    if value > limit // base or (value == limit // base and digit > limit % base):
        raise OverflowError("数值超出范围")
    value = value * base + digit
assert value == limit

如果当前值小于上限除以基数的商,下一步还有空间;如果恰好等于商,就必须进一步比较当前位与余数。这样所有比较都发生在仍可表达的范围内。

Python 本身支持大整数,这里显式设置上限,是为了演示固定宽度目标类型的判断方式。移植到原生代码时,应保证参与比较的变量类型一致,避免有符号与无符号隐式转换改变判断。

字符映射要先于数值累计

面向固定范围的接口,应在逐位累积前检查是否还能乘进制并加入新位。面向超长数字的接口,则可以将结果保留为数字序列,使用另一套容量约束。

两种路线应在接口层明确区分。不要在普通输入时返回整数,遇到大数又无说明地变成其他类型,否则调用方难以建立稳定的处理逻辑。

先确认字符属于当前字母表,再取得数值Python
alphabet = "0123456789ABCDEF"
lookup = {char: index for index, char in enumerate(alphabet)}
assert len(lookup) == len(alphabet)
text = "2AF"
value = 0
for char in text:
    if char not in lookup:
        raise ValueError("字符不属于当前进制")
    value = value * len(alphabet) + lookup[char]
assert value == 687

不能仅凭字符代码落在某个大区间,就假设它是有效数字。字母表中可能包含不连续的范围,大小写也可能表示不同数值。对小字符集,固定查找表可以让规则更直接。

这个片段只演示映射步骤,正式的有界解码还需要结合前面的逐位范围检查。把不同职责分开说明,有助于测试时分别定位是字符不被支持,还是有效数字超出了目标范围。

带符号输入还需要处理不对称边界

如果目标是有符号整数,负方向的绝对值上限通常比正方向多一个单位。可以先保存符号,在无符号容器中累计绝对值,再在成功后恢复符号;最小负值的恢复需要专门处理,不能先构造无法容纳的正值。

空字符串、只有符号、符号出现在中间,以及小数点等都应在格式层明确拒绝。对于只接受无符号标识的接口,直接禁止符号,比解析以后再试图判断负数是否“其实等于零”更简单。

范围检查需要覆盖的相邻值
位置预期
最大允许值减一成功
最大允许值成功
最大允许值加一拒绝
很长但全是前导零由长度规则与数值规则共同决定
中间混入非法字符格式错误而非返回前缀

长度限制不能代替数值检查。同样位数的数字可能一部分有效、一部分越界;反过来,大量前导零又可能让很长的文本仍然代表很小的数值。两类限制各有用途。

不要用饱和结果假装解码成功

零值可能是合法结果,而空输入、非法字符或越界属于失败。调用方需要根据约定区分这些情况,不能仅用结果是否为真来判断解码成功。

测试除了随机往返样例,还应覆盖最大可表示值、紧邻越界值和中途出现非法字符的内容。正确处理边界,往往比追求常见输入路径少一次判断更值得保留。

某些业务会把超限值压到最大值,这应当是明确的业务变换,而不是基础解码器默认行为。对于标识、权限或数量字段,静默饱和可能把两个不同输入变成同一个有效值。

测试可以用高精度参考实现计算预期,再确认固定宽度实现只在允许范围内接受结果。随机生成合法字符串有助于覆盖组合,但边界附近的相邻值仍需要专门列出。

最后检查失败路径是否保留旧输出。更清楚的接口是只有完整成功时才提交最终值,失败则返回独立状态,让调用者不可能误把某个部分累计值继续用于业务。