Go 从不进行隐式类型转换。兼容的类型之间用 T(v) 转换,数字和文本之间用 strconv 包转换。下面是被搜索最多的两种转换:
strconv.Itoa(“integer to ASCII”)把 int 格式化成十进制文本。strconv.Atoi(“ASCII to integer”)把文本解析成 int,文本不是有效数字时返回错误。一定要检查这个错误。
T(v) 转换
Go 的转换语法看起来像一次以目标类型命名的函数调用。它适用于三种情况:
- 数值类型之间(
int、int64、float64、uint8等), string与[]byte或[]rune之间,- 底层类型相同的类型之间(
Celsius和float64)。
仔细看看结果:
int(3.9)是3,int(-3.9)是-3。转换会向零截断,而不是四舍五入。要选择其他行为,先用math.Round、math.Floor或math.Ceil。math.Round遇到 .5 时朝远离零的方向取整,所以-3.5变成-4。uint8(300)是44,因为只保留了低 8 位(300 减 256),int8(200)是-56也是同样的原因。没有错误,也不会 panic。Celsius和float64的底层类型相同,所以可以自由转换,但必须显式转换。
这些值特意放在变量里。如果用常量,同样的转换无法编译。
常量会被检查,变量不会
转换一个放不下的常量会导致编译错误。转换值相同的变量则会悄悄回绕:
fmt.Println(uint8(300)) // compile error
x := 300
fmt.Println(uint8(x)) // 44
./main.go:6:20: constant 300 overflows uint8
把非整数常量转换成整数类型同样会被拒绝,而变量中的相同值则会被截断:
./main.go:7:18: cannot convert -3.9 (untyped float constant) to type int
如果浮点数超出了目标整数类型的范围(比如 int64(1e20)),结果取决于具体实现:例如 amd64 和 arm64 会给出不同的答案。转换不可信的值之前,先检查范围。
数字转字符串
| 源类型 | 函数 | 示例 | 结果 |
|---|---|---|---|
int | strconv.Itoa(n) | strconv.Itoa(-7) | "-7" |
int64 | strconv.FormatInt(n, base) | strconv.FormatInt(255, 16) | "ff" |
uint64 | strconv.FormatUint(n, base) | strconv.FormatUint(5, 2) | "101" |
float64 | strconv.FormatFloat(f, fmt, prec, 64) | strconv.FormatFloat(3.14159, 'f', 2, 64) | "3.14" |
bool | strconv.FormatBool(b) | strconv.FormatBool(true) | "true" |
| 任意类型 | fmt.Sprint(v) | fmt.Sprint(3.5) | "3.5" |
| 任意类型,带格式 | fmt.Sprintf(format, v) | fmt.Sprintf("%05d", 42) | "00042" |
FormatFloat 接收一个格式字节('f' 表示定点,'e' 表示指数形式,'g' 表示取两者中较短的)、一个精度(-1 表示“能精确往返转换的最少位数”),以及原浮点数的位数(64 或 32)。
strconv 比 fmt.Sprint 更快,后者会把值装进接口,再走 fmt 通用的格式化流程。在热点循环中这个差距会显现出来;偶尔转换一下的话,选读起来更顺的那个就行。
字符串转数字
| 目标类型 | 函数 | 返回 |
|---|---|---|
int | strconv.Atoi(s) | int, error |
| 任意有符号整数 | strconv.ParseInt(s, base, bitSize) | int64, error |
| 任意无符号整数 | strconv.ParseUint(s, base, bitSize) | uint64, error |
| 浮点数 | strconv.ParseFloat(s, bitSize) | float64, error |
| 布尔值 | strconv.ParseBool(s) | bool, error |
ParseInt 总是返回 int64。bitSize 参数(0、8、16、32、64)设定它接受的范围,所以把结果转换成更小的类型时保证放得下。base 为 0 表示“根据前缀推断”,所以 0x1f、0o17、0b101 和 1_000 都能解析。
输出说明了这些事情:
" 42"会失败。Atoi只接受一个可选的正负号和数字,其他都不接受。来自bufio的输入通常以\n结尾,所以要先去掉。"4.2"对Atoi来说是无效的。小数请用ParseFloat。- 发生超出范围的错误时,
ParseInt会返回能放得下的最大值(int8为127),同时返回错误。错误不为 nil 时不要使用这个值。 - 这些错误是包装了
strconv.ErrSyntax或strconv.ErrRange的*strconv.NumError值,所以可以用errors.Is区分它们。
ParseBool 接受 1、t、T、TRUE、true、True 以及对应的表示 false 的形式。"yes" 和 "on" 都会报错。
string(65) 陷阱
把整数转换成 string 得到的不是数字字符,而是以该数为 Unicode 码点的那个字符:
n := 65
s := string(n) // "A", not "65"
编译器会接受它,但 go vet 会报告:
./main.go:10:7: conversion from int to string yields a string of one rune, not a string of digits
如果你确实想要那个字符,就用 rune 明确表达:string(rune(n))。如果你想要的是数字文本,用 strconv.Itoa(n)。把 rune 或 byte 转换成 string 没有问题,也很常见:
字符串、字节与 rune
string、[]byte 和 []rune 可以用 T(v) 互相转换。每次转换都会复制数据,因为字符串是不可变的,而切片不是:
需要按字符索引或修改时,转换成 []rune;做 I/O、哈希和字节级修改时,转换成 []byte。rune 和字节一节会详细讲解 UTF-8。
转换与类型断言
转换是把一个值从一种具体类型变成另一种具体类型。从接口(any、error、io.Reader)中取出具体的值是另一种操作,即类型断言:
var v any = 42
n := v.(int) // type assertion: v holds an int, give it to me
f := float64(n) // conversion: int to float64
// f := float64(v) // compile error: cannot convert v (variable of interface type any)
// // to type float64: need type assertion
先断言,再转换。comma-ok 形式 n, ok := v.(int) 可以避免类型不对时 panic。
速查表
| 任务 | 代码 |
|---|---|
| int 转 string | strconv.Itoa(n) |
| int64 转 string | strconv.FormatInt(n, 10) |
| string 转 int | n, err := strconv.Atoi(s) |
| string 转 int64 | n, err := strconv.ParseInt(s, 10, 64) |
| string 转 float64 | f, err := strconv.ParseFloat(s, 64) |
| float64 转 string | strconv.FormatFloat(f, 'f', -1, 64) |
| float64 转 int | int(f)(截断)或 int(math.Round(f)) |
| int 转 float64 | float64(n) |
| string 转 []byte | []byte(s) |
| []byte 转 string | string(b) |
| rune 转 string | string(r) |
| bool 转 string | strconv.FormatBool(b) |
| 任意值转 string | fmt.Sprint(v) |
常见问题
Go 中如何把 int 转成 string?
用 strconv.Itoa(n):strconv.Itoa(42) 返回 "42"。int64 用 strconv.FormatInt(n, 10)。fmt.Sprint(n) 也适用于任何类型,但更慢。不要写 string(n):它会把 n 当作 Unicode 码点,所以 string(65) 是 "A"。
Go 中如何把 string 转成 int?
用 strconv.Atoi,它返回数字和一个错误:
n, err := strconv.Atoi("42")
if err != nil {
// not a valid integer
}
需要指定位数或进制时,用 strconv.ParseInt(s, 10, 64),它返回 int64。Atoi 不会去掉空格,所以 " 42" 会报错;请先调用 strings.TrimSpace。
Go 中如何强制类型转换?
Go 称之为转换,语法是 T(v):float64(n)、int(f)、[]byte(s)。它适用于数值类型之间、字符串与字节切片或 rune 切片之间,以及底层类型相同的类型之间。它不会解析文本:把 "42" 变成 42 需要用 strconv。从接口中取出具体类型是类型断言 v.(T),而不是转换。
Go 中如何把浮点数转成整数?
int(f) 向零截断:int(3.9) 是 3,int(-3.9) 是 -3。要四舍五入,用 int(math.Round(f))。如果浮点数超出了整数类型的范围,结果取决于具体实现,所以当值来自输入时,先检查范围。
为什么 Go 中 string(65) 返回 "A"?
把整数转换成 string 时,它会被解释为 Unicode 码点,而 65 是 A 的码点。go vet 会标记这种写法:conversion from int to string yields a string of one rune, not a string of digits。要得到 "65",用 strconv.Itoa(65)。