fmt.Println是傻瓜式输出,自动加空格换行、不解析格式符;fmt.Printf是精准控制式输出,需格式字符串、不自动换行、严格校验参数类型与顺序。
根本区别就一条:fmt.Println 是“傻瓜式输出”,自动加空格、自动换行、不认格式符;fmt.Printf 是“精准控制式输出”,必须传格式字符串,不自动换行,且严格校验占位符与参数的顺序和类型。
fmt.Println("Name:", name, "Age:", age) → 直接拼出 Name: Alice Age: 30(中间空格+末尾换行)fmt.Printf("Name: %s, Age: %d\n", name, age) → 同样输出,但若漏写 \n,下一行就会紧贴着它打印fmt.Printf("%s %d", 42, "hello") 直接崩溃,因为 %s 期待字符串却收到整数fmt.Println("Value: %d", 42) 不会解析 %d,原样输出 Value: %d 42
别凭感觉选,按场景卡死规则:
user、err、len(data))→ 无脑用 fmt.Println,省得想格式符fmt.Printf,例如:fmt.Printf("status=%s, code=%d, took=%.3fms\n", status, code, elapsed.Seconds()*1000)
%+v:fmt.Printf("user=%+v\n", user),比 fmt.Println(user) 多一层可读性fmt.Printf + 条件包裹,也别让 fmt.Println 刷屏——它每次调用都触发一次系统写入,性能敏感时明显拖慢新手常以为 println(内置函数)和 fmt.Println 只是写法不同,其实它们输出目标完全不同:
println("debug") 输出到 stderr,不带缓冲,可能和 stdout 输出乱序;fmt.Println 走的是 stdout,带缓冲,顺序稳定println / print,它们不是标准库函数,不能重定向、不能测试、不支持 io.Writer,连 go vet 都会警告runtime.Caller 或直接上 log 包;硬凑 fmt.Printf("[%s:%d] %v\n", filepath.Base(__FILE__), __LINE__, x) 看似聪明,实则引入了未声明的包依赖和编译错误风险如果需求超出了终端打印——比如

fmt.Printf 就不够用了,必须升级到 fmt.Fprintf:
fmt.Fprintf(os.Stderr, "error: %v\n", err) → 把错误定向到 stderr(符合 Unix 习惯)fmt.Fprintf(w, "Content-Type: text/plain\n\nHello %s", name) → 在 HTTP handler 中安全写响应buf := &bytes.Buffer{}; fmt.Fprintf(buf, "%d,%s", id, name) → 内存中构造字符串,避免反复分配Fprintf 返回 (n int, err error),文件写满、磁盘只读等错误必须检查,不能像 Println 那样忽略最常被低估的一点:所有 fmt 函数底层都调用 Fprint 系列,Println 是封装好的“快捷键”,Printf 是带解析器的“手动档”,而 Fprintf 才是那个能插进任意管道的“万能接口”。别让调试习惯*正式输出逻辑。