登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go math/big.Int.SetString 如何判断进制输入失败

来源:17golang原创

时间:2026-09-15 03:55:38 334浏览 收藏

math/big.Int 解析订单号、配置值或外部接口传来的超长整数时,最容易误判的是:看到返回了一个 *Int,就以为解析成功。SetString 实际返回两个值,只有第二个值为 true 才能使用第一个值;失败时返回 nil, false,接收者的值也不能继续读取。

要点速览
  • 判断结果看 ok,不要用 n != nil 代替完整判断。
  • base=0 会识别 0b0o0x 前缀;固定进制不会按这套规则处理。
  • SetString 要求整个字符串合法,尾部多一个字母、空格或错误下划线都可能让解析失败。

先把输入约定和返回值接对

先决定调用方的进制策略:数据格式明确是十进制,就传 10;需要接受带前缀的用户输入,才传 0。不要把“固定十六进制”和“自动识别前缀”混在一个入口里,否则同一串字符在不同调用路径上会得到不同结论。

最小安全写法如下。这里把解析结果和业务错误分开,失败分支不会触碰未定义的整数值。

package main

import (
	"fmt"
	"math/big"
)

func parseBigInt(text string, base int) (*big.Int, error) {
	// SetString 的第二个返回值才是成功标志;失败时 n 为 nil。
	n, ok := new(big.Int).SetString(text, base)
	if !ok {
		// 把原始输入和进制带回上层,便于定位调用约定问题。
		return nil, fmt.Errorf("invalid integer %q for base %d", text, base)
	}
	return n, nil
}

func main() {
	// base=0 让前缀决定进制;没有前缀时按十进制处理。
	n, err := parseBigInt("0x2a", 0)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(n.String()) // 预期得到 42
}
Go math big Int SetString 返回契约静态关系图,展示输入字符串、base、解析方法与成功或 nil false 返回边界
图1:SetString 返回契约示意图,先看 bool 再决定是否使用 *Int;这是结构说明,不是真实运行截图。

如果只写 n, _ := ...,语法上虽然成立,但会把输入错误静默吞掉。更隐蔽的错误是先创建一个 z,调用 z.SetString 失败后仍打印 z.String():官方文档明确说明失败时接收者值未定义,不能把它当作零值或旧值。

base=0 和固定进制不要混用

base0 时,0b/0B 选择二进制,0o/0O 或单独的 0 选择八进制,0x/0X 选择十六进制;没有这些前缀时按十进制解释。此时下划线可以出现在前缀与数字之间,或连续数字之间。

传入固定进制后,规则就不同了。比如 SetString("0x2a", 16) 不应被当成自动识别调用;如果输入协议要求十六进制,建议先明确约定是否允许前缀,再选择统一的清洗或拒绝策略。固定进制下,下划线也不是分隔符,而是无效字符。

调用判断原因
SetString("42", 10)成功全部字符都是十进制数字
SetString("0x2a", 0)成功前缀选择十六进制
SetString("101", 2)成功字符只包含二进制数字
SetString("12z", 10)失败字母 z 不是十进制数字,且尾部不能被忽略
SetString("1_000", 10)失败固定进制不识别下划线
Go math big Int SetString 进制输入边界图,对比 base 0 前缀规则与固定进制的有效字符和完整字符串要求
图2:进制输入边界示意图,区分自动识别前缀与固定进制解析;图中关系用于解释规则,不代表执行流程。

把失败分支变成可定位的业务错误

在 HTTP 参数、配置文件或批处理导入中,失败信息至少应包含输入所在字段和采用的进制策略。不要把完整原始值直接写入敏感日志;对普通业务号可以保留脱敏后的片段,对外返回则使用稳定的错误码。

func parseDecimalID(text string) (*big.Int, error) {
	// 这个入口只接受纯十进制,避免调用者借 base=0 偷渡前缀。
	n, ok := new(big.Int).SetString(text, 10)
	if !ok {
		return nil, fmt.Errorf("id must be a decimal integer")
	}
	return n, nil
}

func parsePrefixedInteger(text string) (*big.Int, error) {
	// 这个入口明确支持 0b、0o、0x 和无前缀十进制。
	n, ok := new(big.Int).SetString(text, 0)
	if !ok {
		return nil, fmt.Errorf("integer has an invalid prefix or digit")
	}
	return n, nil
}

这种分入口方式比“先猜用户想要哪种进制”更容易测试。测试表应至少覆盖合法值、空字符串、错误数字、前缀与 base 不匹配、错误下划线以及合法数字后追加脏字符。尤其要保留“完整字符串必须合法”这一条,否则调用方可能把 123abc 当成 123 接受。

常见问题

SetString 失败时返回的是零值吗?

不是。失败返回的整数指针为 nil,接收者值未定义。业务代码应直接处理 ok == false,不要读取失败后的 z

base=0 没有前缀时是什么进制?

没有二进制、八进制或十六进制前缀时,按十进制解释。需要严格十进制协议时,仍建议直接传 10,这样约定更清楚。

固定 base=16 能直接传 0x 前缀吗?

不要依赖这种写法。前缀推断属于 base=0 的规则;固定进制入口应先规定是否允许前缀,并在入口统一处理或拒绝它。

为什么不能只判断解析出的数是不是 0?

因为合法输入 0 本身就是零,而非法输入也可能被错误地当成零。区分成功与失败必须使用 SetString 返回的布尔值。

记住一句判断顺序就够了:先决定 base 的协议,再接收 n, ok,最后只在 ok 为真时使用整数。这样大整数解析的失败就会停在输入边界,而不会扩散到后续计算。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>