Go的println和fmt.Println有什么区别
Go 内置的 println 输出到 stderr,fmt.Println 输出到 stdout。println 不能打印结构体、切片输出地址,还可能被废弃,只适合临时 debug。生产代码必须用 fmt.Println,功能完整、格式化支持好。
刚入门 Go 的人经常会分不清 println 和 fmt.Println,用哪个都差不多嘛。其实差别挺大,一个是临时 debug 用的,一个才是生产级别的。
区别一:输出目标不同
println 输出到标准错误流(stderr)。
fmt.Println 输出到标准输出流(stdout)。
这在脚本里就能看出来:
# 假设 Go 程序用 println 输出"hello"
go run main.go 2>/dev/null # 看不到"hello"(stderr 被丢弃了)
go run main.go 1>/dev/null # 能看到"hello"(stdout 被丢弃了)
所以如果你在脚本里管道输出给另一个命令,千万别用 println,数据会走 stderr 被吞掉。
区别二:功能完整度
println 功能简陋,它只能接收多个参数然后用空格分隔输出,别想着格式化。
println("hello", 42, 3.14) // hello 42 3.14
println("hello %s", "world") // hello %s world(不会格式化)
fmt.Println 功能齐全,还有 fmt.Printf 支持格式化字符串。
fmt.Println("hello", 42, 3.14) // hello 42 3.14
fmt.Printf("hello %s\n", "world") // hello world(正确格式化)
fmt.Sprintf("hello %s", "world") // 返回格式化后的字符串
区别三:复杂数据类型的输出
println 对结构体和切片的输出很"朴素",基本看不出啥东西。
type Person struct {
Name string
Age int
}
p := Person{"Alice", 30}
println(p) // {Alice 30}
s := []int{1, 2, 3}
println(s) // [1 2 3](实际上是个地址)
fmt.Println 稍好点,还有更专业的 fmt.Sprintf 配合 %v / %+v / %#v 来做深度对象检查。
p := Person{"Alice", 30}
fmt.Println(p) // {Alice 30}
fmt.Printf("%+v\n", p) // {Name:Alice Age:30}(带字段名)
fmt.Printf("%#v\n", p) // main.Person{Name:"Alice", Age:30}(Go 代码形式)
s := []int{1, 2, 3}
fmt.Println(s) // [1 2 3]
fmt.Printf("%v\n", s) // [1 2 3]
fmt.Printf("%#v\n", s) // []int{1, 2, 3}
真要深入调试对象,还可以用第三方库 litter 或 spew。
区别四:性能和地位
println 是内置函数,不需要导包,但文档明确说了"for debugging purposes"(用来 debug 的)。
fmt.Println 需要 import fmt 包,是官方推荐的标准方式,功能完整、文档齐全。
从 Go 官方的态度来看,println 就是个"方便调试的玩意儿",随时可能被调整甚至废弃。生产代码千万别赖上它。
什么时候用 println
只有一个场景:快速临时 debug。
func processData(data []int) {
println("data:", data) // 快速看一眼
// ...
}
func main() {
x := 42
println("x is:", x) // 快速看一眼
}
而且要记得后来删掉,不能留在代码里提交。
# 什么时候用 fmt.Println
**一切生产代码都应该用 fmt.Println**。
```go
// 日志输出
fmt.Println("[INFO] 程序启动")
// 用户交互
fmt.Println("请输入你的名字:")
// 格式化输出
fmt.Printf("用户 %s,年龄 %d\n", name, age)
要定制日志格式的话,通常会用 log 包或者第三方库(logrus、zap 等)。
import "log"
log.Println("[INFO] 程序启动")
log.Printf("用户 %s 登录\n", username)
log 包内置了时间戳、日志级别等功能,比 fmt 更适合日志。
完整对比表
| 特性 | println | fmt.Println |
|---|---|---|
| 输出目标 | stderr | stdout |
| 需要导包 | 否 | 是(import fmt) |
| 格式化 | 不支持 | 支持(Printf) |
| 结构体输出 | 简陋 | 较好(%v 等) |
| 文档地位 | Debug 用 | 标准输出 |
| 生产推荐度 | ❌ 不推荐 | ✅ 强烈推荐 |
| 性能 | 稍快(内置) | 稍慢(包函数) |
小结
println 是 Go 给你的一个"快速 debug 便利函数",输出到 stderr,功能简单,只适合临时调试。
fmt.Println 是生产级别的标准输出,输出到 stdout,功能完整,支持格式化,是正经代码的标准写法。
简单判断:看到 println 在代码里,说明这是有人临时加上去 debug 的,应该删掉;看到 fmt.Println,才是真正的业务逻辑输出。
