深色模式
Go 数字与字符串转换
概述
int 和 string 互转是 Go 里最常见的小操作之一,但越是常见,越容易写得随手。最典型的两种情况是:本来应该用 strconv,却顺手写成了 fmt.Sprintf;本来需要处理进制和位宽,却直接上了 Atoi。
这篇文章只做一件事:把数字和字符串转换里最常见的几个入口摆清楚,避免把简单问题写得模糊。
int 转 string
strconv.Itoa
如果手里就是一个 int,最直接的写法是 strconv.Itoa:
go
num := 123
str := strconv.Itoa(num)它的名字其实就是 integer to ASCII 的缩写,语义非常明确。
strconv.FormatInt
如果手里是 int64,或者需要明确指定进制,用 FormatInt:
go
num := int64(123)
str := strconv.FormatInt(num, 10)第二个参数表示进制。除了十进制,也可以写成二进制、十六进制:
go
strconv.FormatInt(15, 2) // "1111"
strconv.FormatInt(15, 16) // "f"fmt.Sprintf
fmt.Sprintf 当然也能做转换:
go
str := fmt.Sprintf("%d", 123)但它更适合格式化输出,而不是单纯转换。只做数字转字符串时,strconv 的语义更直接,性能通常也更好。
string 转 int
strconv.Atoi
如果字符串是普通十进制整数,Atoi 最方便:
go
num, err := strconv.Atoi("123")
if err != nil {
return err
}它的适用范围很明确:只适合普通十进制整数字符串。
strconv.ParseInt
如果需要控制进制或位宽,用 ParseInt:
go
num64, err := strconv.ParseInt("7b", 16, 64)
if err != nil {
return err
}这里三个参数分别是:
- 原始字符串
- 进制
- 位宽
返回值固定是 int64,如果最终只想要 int,需要再做一次转换:
go
num := int(num64)位宽和进制不是装饰参数
ParseInt 的第三个参数不是随手填的。它决定了解析结果能落到多大范围里。
例如:
go
num8, err := strconv.ParseInt("127", 10, 8)这里允许的结果范围就是 int8。如果字符串超出范围,函数会返回错误,而不是悄悄截断。
这也是为什么带边界要求的解析逻辑里,ParseInt 往往比 Atoi 更合适。
什么时候选 fmt,什么时候选 strconv
这个判断其实不复杂:
- 只是转换:优先
strconv - 还要顺便拼其他文本:可以用
fmt.Sprintf
例如日志输出:
go
msg := fmt.Sprintf("port=%d", port)这里用 fmt 很自然,因为它不只是做转换,还在做整段字符串格式化。
但如果只是:
go
idStr := fmt.Sprintf("%d", id)那就通常没必要绕一圈。
两个高频坑
不处理错误
从字符串转数字一定要处理错误。"12a"、空字符串、溢出值,都不是少见情况。
混淆 int 和 int64
strconv.Atoi 返回的是 int,strconv.ParseInt 返回的是 int64。如果后续要跟数据库 ID、时间戳或协议字段打交道,最好一开始就把目标类型想清楚,不要一边写一边到处强转。
一段更接近日常业务的例子
go
func parsePage(raw string) (int, error) {
if raw == "" {
return 1, nil
}
page, err := strconv.Atoi(raw)
if err != nil {
return 0, fmt.Errorf("非法页码 %q: %w", raw, err)
}
if page <= 0 {
return 0, fmt.Errorf("页码必须大于 0")
}
return page, nil
}这类逻辑里,真正重要的通常不是“怎么转”,而是“转完后的边界怎么兜住”。
