Go 从 0 到精通 · 第 03 课:输入输出与类型转换
Go 从 0 到精通 · 第 03 课:输入输出与类型转换
学习定位:这是整套 Go 教程的第 3 课。
前置要求:已经完成第 2 课,理解了变量声明(var、:=)、常量(const)、零值机制和常见基础数据类型。
本课目标:掌握控制台输出的三种方式、格式化动词、控制台输入读取,以及 Go 中显式类型转换的规则和常见用法。
1. 本课你要解决的核心问题
这一课的重点是让你的程序从"只能输出固定文字"变成"能和用户交互"。
你需要搞明白以下几个关键问题:
fmt.Print、fmt.Println、fmt.Printf到底有什么区别- 格式化动词是什么意思,常见的有哪些
- 怎么从控制台读取用户输入
- 为什么 Go 的类型转换必须显式进行
int、float64、string之间怎么互相转换- 转换过程中有哪些常见坑
把这些搞清楚,你写出来的程序就不再只是"输出一句话",而是一个能和用户对话的小程序了。
2. 控制台输出:三种方式的区别
Go 中最常用的输出函数有三个,都来自 fmt 包:
fmt.Printfmt.Printlnfmt.Printf
它们看起来很像,但行为不同。你必须把它们分清楚。
2.1 fmt.Print:原样输出,不换行
1 | package main |
输出:
1 | 你好世界 |
特点:
- 输出内容后不会自动换行
- 多次调用时,内容会紧紧连在一起
什么时候用:
- 你需要精确控制输出格式
- 不希望自动添加换行符
2.2 fmt.Println:输出后自动换行
1 | package main |
输出:
1 | 你好 |
特点:
- 输出内容后会自动加一个换行符
- 如果传入多个参数,参数之间会自动加空格
例如:
1 | package main |
输出:
1 | 姓名: Tom 年龄: 18 |
这是日常开发中用得最多的输出函数。
2.3 fmt.Printf:格式化输出
1 | package main |
输出:
1 | 我叫 Tom,今年 18 岁 |
特点:
- 第一个参数是格式化字符串,里面用
%开头的"占位符"表示变量的位置 - 后面的参数按顺序填入占位符
- 不会自动换行,需要手动加
\n
这是你想精确控制输出格式时最强大的工具。
2.4 一句话区分三者
fmt.Print:原样输出,不换行fmt.Println:输出后换行,多参数之间加空格fmt.Printf:按格式化模板输出,不自动换行
3. 格式化动词详解
fmt.Printf 的核心在于格式化动词。你不需要一次性全记住,但下面这些必须先掌握。
3.1 常用格式化动词
| 动词 | 含义 | 示例值 | 输出结果 |
|---|---|---|---|
%d |
十进制整数 | 18 |
18 |
%f |
浮点数 | 3.14 |
3.140000 |
%s |
字符串 | "Tom" |
Tom |
%t |
布尔值 | true |
true |
%v |
通用格式(自动判断) | 任意 | 值的默认表示 |
%T |
打印变量的类型 | 18 |
int |
%% |
输出一个字面的 % |
— | % |
3.2 综合示例
1 | package main |
输出:
1 | 姓名:小明 |
3.3 控制浮点数精度
默认情况下 %f 会输出 6 位小数。如果你想控制小数位数,可以这样写:
1 | package main |
输出:
1 | 价格:19.99 |
格式说明:
%.2f表示保留 2 位小数%.1f表示保留 1 位小数%.0f表示不要小数部分
这在输出金额、百分比等场景非常实用。
3.4 %v 和 %T:两个万能工具
当你不确定用什么格式化动词时,%v 是一个通用选择:
1 | package main |
%v 会根据值的类型自动选择合适的输出方式。
而 %T 可以打印变量的类型,这在调试时非常有用:
1 | package main |
输出:
1 | a 的类型是 int |
建议你在学习阶段经常用 %T 来确认变量的实际类型,这对加深理解非常有帮助。
4. fmt.Sprintf:格式化但不输出
有时候你想把格式化的结果保存到一个字符串里,而不是直接打印出来。
这时候用 fmt.Sprintf:
1 | package main |
输出:
1 | 姓名:Tom,年龄:18 |
Sprintf 和 Printf 的区别:
Printf:格式化后直接打印到控制台Sprintf:格式化后返回字符串,不打印
这在拼接日志、构造消息文本时非常常用。
5. 控制台输入:让程序"听"用户说话
前面两课你的程序都是"自说自话"。从这一节开始,你的程序可以接收用户输入了。
5.1 fmt.Scan:最基本的输入方式
1 | package main |
运行时,程序会等你在控制台输入内容,按回车后继续执行。
例如你输入 Tom,输出就是:
1 | 请输入你的名字:Tom |
5.2 关于 & 符号
你注意到 fmt.Scan(&name) 里有一个 &。
这个 & 表示取变量的地址。你可以先这样理解:
fmt.Scan需要知道"把用户输入的值放到哪里",所以你要告诉它变量的地址。
如果不加 &,fmt.Scan 拿不到变量地址,就无法把输入值写入变量。
指针的详细内容我们后面专门有一课讲,这里你只需要记住:
用
fmt.Scan读取输入时,变量前面要加&。
5.3 读取多个输入
1 | package main |
运行时输入:
1 | Tom 18 |
输出:
1 | 你好 Tom,你今年 18 岁 |
fmt.Scan 默认以空格或换行作为分隔符。
5.4 fmt.Scanf:按格式读取
1 | package main |
fmt.Scanf 和 fmt.Printf 类似,第一个参数是格式化模板。
不过在实际开发中,fmt.Scanf 用得相对少一些,因为控制台输入通常比较简单,fmt.Scan 或 fmt.Scanln 已经够用。
5.5 fmt.Scanln:读取一行输入
1 | package main |
fmt.Scanln 和 fmt.Scan 很像,区别在于:
fmt.Scan:以空格或换行分隔fmt.Scanln:遇到换行符就停止读取
在只需要读一行简单输入的场景下,fmt.Scanln 更直观。
5.6 三种输入函数对比
| 函数 | 分隔方式 | 适用场景 |
|---|---|---|
fmt.Scan |
空格或换行 | 读取多个值,最通用 |
fmt.Scanln |
换行 | 读取一行输入 |
fmt.Scanf |
按格式模板 | 需要严格格式匹配 |
6. 一个完整的交互式小程序
把前面学到的输出和输入结合起来,写一个完整的交互程序:
1 | package main |
运行效果:
1 | 请输入你的姓名:小明 |
这就是一个有输入、有输出、有格式化的完整小程序。
7. 类型转换:Go 的显式原则
从这一节开始,我们进入本课的另一个重点:类型转换。
7.1 为什么 Go 的类型转换必须显式
在某些语言里,不同数值类型之间可以自动转换。例如把一个整数赋给浮点变量,编译器会"悄悄"帮你做。
但 Go 不会。
Go 的原则是:
类型转换必须由你显式写出来,编译器不会替你做隐式转换。
这样做的好处是:
- 代码意图更清楚
- 不容易出现精度丢失等隐蔽问题
- 团队协作时更容易审查
7.2 基本语法
Go 的类型转换语法是:
1 | 目标类型(要转换的值) |
例如:
1 | float64(age) |
注意,这不是函数调用,而是 Go 的类型转换表达式。
8. 整数与浮点数之间的转换
这是最常见的类型转换场景。
8.1 int 转 float64
1 | package main |
输出:
1 | 18 |
这种转换是安全的,不会丢失信息。
8.2 float64 转 int
1 | package main |
输出:
1 | 19 |
注意:这里 19.99 变成了 19,小数部分被直接截断,不是四舍五入。
这是一个非常重要的特点:
float64转int时,小数部分会被截断丢弃。
如果你需要四舍五入,需要自己处理,例如先加 0.5 再转换,或者使用 math.Round。
8.3 不同整数类型之间的转换
1 | package main |
即使 int 和 int64 看起来很相似,Go 也不允许你直接混用,必须显式转换。
这是 Go 严格类型系统的体现。
9. 字符串与数字之间的转换
这是初学者最容易搞错的部分。
关键前提:字符串和数字之间的转换,不能直接用 int() 或 string() 完成。你需要使用 strconv 包。
9.1 为什么不能直接用 string(65)
先看一个很多新手会踩的坑:
1 | package main |
你可能期望输出 "65",但实际输出是:
1 | A |
原因是:string(65) 的含义是"把数字 65 当成 Unicode 码点,转换成对应的字符"。65 对应的字符是 A。
这不是 bug,而是 Go 的设计。
所以,把数字变成字符串,不能用 string(),要用 strconv 包。
9.2 整数转字符串:strconv.Itoa
1 | package main |
输出:
1 | 年龄是:18 |
Itoa 的名字来自 “Integer to ASCII”,是整数转字符串的标准方式。
9.3 字符串转整数:strconv.Atoi
1 | package main |
输出:
1 | 数字是: 42 |
注意 strconv.Atoi 返回两个值:
- 第一个是转换结果
- 第二个是错误信息
如果输入的字符串不是合法数字,err 就不会是 nil。
例如:
1 | package main |
输出:
1 | 转换失败: strconv.Atoi: parsing "abc": invalid syntax |
这就是 Go 显式错误处理的体现。你在上一课预告里已经知道 Go 喜欢"把错误暴露出来",这里就是一个典型例子。
9.4 浮点数与字符串之间的转换
浮点数转字符串用 strconv.FormatFloat,字符串转浮点数用 strconv.ParseFloat。
浮点数转字符串
1 | package main |
输出:
1 | 价格是:19.99 |
FormatFloat 的参数说明:
- 第 1 个参数:要转换的浮点数
- 第 2 个参数:格式,
'f'表示普通小数格式 - 第 3 个参数:精度(小数位数),
-1表示自动 - 第 4 个参数:位数,
64表示float64
初学阶段你可以先记住这个固定写法,后面用多了自然就熟了。
不过在简单场景下,用 fmt.Sprintf 也可以达到类似效果:
1 | priceStr := fmt.Sprintf("%.2f", price) |
这种写法更简洁,实际开发中也很常用。
字符串转浮点数
1 | package main |
输出:
1 | 数值是: 3.14 |
ParseFloat 的第二个参数 64 表示结果精度为 float64。
9.5 布尔值与字符串之间的转换
虽然不算特别常用,但了解一下也有好处。
1 | package main |
输出:
1 | true |
ParseBool 能识别的字符串包括 "true"、"false"、"1"、"0" 等。
10. 类型转换速查表
把本课涉及的常见转换整理成一张表:
| 转换方向 | 方法 | 示例 |
|---|---|---|
int → float64 |
float64(n) |
float64(18) |
float64 → int |
int(f) |
int(19.99) → 19 |
int → int64 |
int64(n) |
int64(100) |
int → string |
strconv.Itoa(n) |
strconv.Itoa(18) → "18" |
string → int |
strconv.Atoi(s) |
strconv.Atoi("42") → 42, nil |
float64 → string |
strconv.FormatFloat 或 fmt.Sprintf |
fmt.Sprintf("%.2f", 19.99) |
string → float64 |
strconv.ParseFloat(s, 64) |
strconv.ParseFloat("3.14", 64) |
bool → string |
strconv.FormatBool(b) |
strconv.FormatBool(true) → "true" |
string → bool |
strconv.ParseBool(s) |
strconv.ParseBool("true") → true, nil |
11. 常见坑总结
11.1 Printf 忘记加 \n
1 | fmt.Printf("你好 %s", name) |
输出会挤在一行:
1 | 你好 Tom年龄 18 |
Printf 不会自动换行,必须手动加 \n。
11.2 格式化动词和变量类型不匹配
1 | age := 18 |
%s 是字符串格式化动词,但 age 是 int。虽然程序可能不会直接崩溃,但输出结果会不正确。
正确写法:
1 | fmt.Printf("年龄:%d\n", age) |
建议:不确定的时候用 %v,它是通用的。
11.3 string(数字) 不是数字转字符串
这一点前面已经重点讲过。再强调一次:
1 | string(65) // 结果是 "A",不是 "65" |
数字转字符串,用 strconv.Itoa 或 fmt.Sprintf。
11.4 Atoi 的返回值不能忽略错误
1 | n, _ := strconv.Atoi(someInput) |
虽然用 _ 可以忽略错误,但在实际开发中这样做很危险。如果输入不是合法数字,n 会是 0,你的程序可能会用一个错误的值继续运行。
正确做法是检查 err:
1 | n, err := strconv.Atoi(someInput) |
11.5 float64 转 int 是截断不是四舍五入
1 | int(2.9) // 结果是 2,不是 3 |
如果你需要四舍五入,可以这样做:
1 | package main |
输出:
1 | 3 |
11.6 Scan 读取输入时忘记加 &
1 | var name string |
正确写法:
1 | fmt.Scan(&name) |
没有 &,Scan 无法把值写入变量。
12. 一段综合示例
把本课所有核心知识串到一个程序里:
1 | package main |
运行效果:
1 | === Go 第 3 课 === |
13. 本课练习
一定要亲手写,不要只看。
练习 1:三种输出方式对比
要求:
- 分别用
fmt.Print、fmt.Println、fmt.Printf输出同一句话 - 观察输出结果的区别
目的:直观感受三者的差异。
练习 2:格式化输出个人信息
要求:
- 定义姓名、年龄、身高(浮点数)、是否在职(布尔值)
- 用
fmt.Printf格式化输出,身高保留 1 位小数 - 用
%T输出每个变量的类型
练习 3:写一个简单计算器
要求:
- 从控制台读取两个整数
- 打印它们的和、差、积
- 使用
fmt.Printf格式化输出
例如输入 10 和 3,输出:
1 | 10 + 3 = 13 |
练习 4:体验 string(数字) 的陷阱
要求:
- 用
string(72)看看输出是什么 - 再用
strconv.Itoa(72)看看输出是什么 - 对比两者,自己总结区别
练习 5:字符串和数字互转
要求:
- 定义一个字符串
"100" - 把它转成整数
- 加上
50 - 再把结果转回字符串
- 输出最终结果
重点体会:为什么 Go 不允许直接用 "100" + 50。
练习 6:浮点数截断体验
要求:
- 定义一个
float64变量,值为9.99 - 把它转成
int - 打印结果,观察是否是四舍五入
- 然后用
math.Round实现四舍五入,再转成int,对比结果
14. 自测题
不看文档,试着回答:
14.1 概念题
fmt.Print、fmt.Println、fmt.Printf的区别是什么?%d、%f、%s、%t、%v、%T分别表示什么?%.2f是什么意思?fmt.Sprintf和fmt.Printf有什么区别?fmt.Scan里为什么变量前面要加&?- 为什么 Go 不允许隐式类型转换?
string(65)的结果是什么?为什么?strconv.Atoi为什么返回两个值?float64转int时小数部分会怎样?- 整数转字符串应该用什么函数?
如果你能流畅回答这些问题,说明这一课你已经真正理解了。
15. 本课总结
这一课你学到的不只是几个函数名,而是 Go 输入输出和类型系统的核心逻辑。
你现在应该已经理解:
fmt.Print不换行,fmt.Println换行,fmt.Printf按格式输出- 格式化动词是
Printf的核心,%d整数、%f浮点、%s字符串、%v通用、%T类型 fmt.Sprintf返回格式化字符串而不打印fmt.Scan、fmt.Scanln、fmt.Scanf用于从控制台读取输入- Go 的类型转换必须显式,编译器不会替你做隐式转换
- 数值类型之间直接用
目标类型(值)转换 - 字符串和数字之间的转换要用
strconv包 string(数字)不是数字转字符串,而是转成 Unicode 字符Atoi和ParseFloat都会返回错误值,必须处理
这一课内容比较多,但每一块都非常实用。从这一课开始,你已经能写出有输入、有输出、有格式化、有类型转换的完整小程序了。
16. 下一课预告
下一课我们进入:条件判断 if 与 switch。
会重点讲:
if的基本用法和 Go 的特殊写法if初始化语句是什么else if和else的使用switch的基本用法和它与其他语言的区别- 为什么 Go 的
switch不需要break - 条件判断中的常见坑
学完下一课,你的程序就能"做决定"了,不再是从头到尾直线执行。





