Go语言中三个输入函数(scanf,scan,scanln)的区别
在 Go 语言中,fmt 包提供了三种输入函数:Scanf、Scan 和 Scanln。这三个函数都是用于从标准输入读取数据并存储到变量中,但是它们在处理输入的方式上有所不同。下面详细解读每个函数的特点和用法。
1. fmt.Scanf
- 用法:fmt.Scanf(format string, a ...interface{}) (n int, err error)
- 功能:根据格式化字符串 format 从标准输入读取数据,并将数据存储到提供的参数中。格式化字符串类似于 C 语言中的 scanf,例如 %d 表示读取一个整数,%s 表示读取一个字符串。
- 输入终止条件:Scanf 读取输入时,会忽略空白字符,并且根据格式化字符串解析输入内容。输入可以包含换行符,但解析会按照格式字符串中的要求进行。
示例代码:
package main
import (
"fmt"
)
func main() {
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(格式如:123 hello):")
fmt.Scanf("%d %s", &a, &b) // 根据格式化字符串读取
fmt.Println("整数:", a)
fmt.Println("字符串:", b)
}
注意:
- 如果输入的格式与指定的格式字符串不匹配,可能会导致读取失败或产生意外结果。
2. fmt.Scan
- 用法:fmt.Scan(a ...interface{}) (n int, err error)
- 功能:读取由空格分隔的输入数据,并将数据存储到提供的参数中。它不需要格式化字符串,而是依次将输入的值赋给传入的变量。
- 输入终止条件:输入以空格或换行符为分隔符,读取的值必须与传入的参数数量匹配。
示例代码:
package main
import (
"fmt"
)
func main() {
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(用空格分隔):")
fmt.Scan(&a, &b) // 直接读取输入到变量中
fmt.Println("整数:", a)
fmt.Println("字符串:", b)
}
注意:
- 输入数据必须能正确解析为指定的变量类型,否则会出现错误。
- 如果输入的值数量与变量数量不匹配,程序会等待进一步的输入。
3. fmt.Scanln
- 用法:fmt.Scanln(a ...interface{}) (n int, err error)
- 功能:与 fmt.Scan 类似,但会在读取完所有指定的参数后检查是否有多余的输入。它要求读取的最后一个输入参数必须位于行的末尾,否则会返回一个错误。
- 输入终止条件:读取到换行符结束,并要求读取的参数数量与传入的变量数量匹配。
示例代码:
package main
import (
"fmt"
)
func main() {
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(用空格分隔,并按回车结束):")
fmt.Scanln(&a, &b) // 读取输入到变量中,并检查换行
fmt.Println("整数:", a)
fmt.Println("字符串:", b)
}
注意:
- 如果输入的值数量与传入的变量数量不匹配,或者在读取所有参数后还有多余的输入,则会返回错误。
总结比较
- Scanf 更加灵活,可以指定格式,但要求输入格式必须匹配。
- Scan 和 Scanln 更适合简单的输入读取,Scanln 还会检查是否有多余的输入。
在实际应用中,根据需要选择适当的函数来读取用户输入。
这三种函数,都会将换行符,一起读取吗?
在 Go 语言中,fmt.Scanf、fmt.Scan 和 fmt.Scanln 这三个函数处理换行符的方式有所不同。让我们详细解释它们对换行符的处理:
1. fmt.Scanf
- 换行符处理:Scanf 不会将换行符视为特殊的输入结束标记,它会根据指定的格式化字符串来解析输入数据。
- 输入中包含换行符:如果换行符符合格式化字符串的要求,它会被视为空白字符而被忽略,否则会被保留在输入流中等待下一次读取。
示例:
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(如:123 hello):")
fmt.Scanf("%d %s", &a, &b) // 如果输入 "123\nhello",换行符会被忽略
在这个例子中,输入时如果用户输入 "123\nhello",换行符会被忽略,因为它只是分隔了两个数据。
2. fmt.Scan
- 换行符处理:Scan 将换行符视为空白字符,与空格一样被忽略。它会继续读取输入,直到满足所有变量的赋值。
- 连续输入的情况:即使输入中有换行符,Scan 仍然会尝试从输入流中读取更多的数据,直到满足要求的变量数量。
示例:
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(用空格分隔):")
fmt.Scan(&a, &b) // 如果输入 "123\nhello",换行符会被忽略
在这个例子中,输入 "123\nhello" 时,Scan 会忽略换行符并继续读取输入,直到读取到字符串 "hello"。
3. fmt.Scanln
- 换行符处理:Scanln 会将换行符作为输入的结束标志。当所有的输入参数都读取完毕后,Scanln 要求输入流中不能有多余的数据。如果有多余的数据,即使是换行符,也会返回错误。
- 换行符终止:它要求最后一个输入参数之后必须是换行符,否则会报错。
示例:
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(用空格分隔,并按回车结束):")
fmt.Scanln(&a, &b) // 如果输入 "123 hello\n",换行符作为结束标志
在这个例子中,输入 "123 hello\n" 时,Scanln 会正常读取到整数和字符串,并认为换行符是输入的结束标志。
总结
- Scanf:根据格式化字符串解析输入,换行符如果不影响解析会被忽略。
- Scan:将换行符视为空白字符,会继续读取更多数据,直到满足所有变量的赋值要求。
- Scanln:以换行符作为输入结束标志,如果在所有参数读取完毕后还有多余输入(除了换行符),会返回错误。
换行符是否被读取取决于函数的设计和用法,Scanln 是唯一一个明确要求换行符作为结束标志的函数。
输入中包含换行符:如果换行符符合格式化字符串的要求,它会被视为空白字符而被忽略,否则会被保留在输入流中等待下一次读取。
对上面加粗部分的解释:
为了更好地理解 fmt.Scanf 如何处理输入中的换行符,下面举一个具体的例子来说明这种行为。
例子
package main
import (
"fmt"
)
func main() {
var a int
var b string
fmt.Print("请输入一个整数和一个字符串(格式:123 hello):")
fmt.Scanf("%d %s", &a, &b)
fmt.Println("a:", a)
fmt.Println("b:", b)
}
示例 1:输入 "123 hello\n"
- 输入内容:123 hello,然后按下 Enter(实际上输入的是 "123 hello\n")。
- 执行过程:
a.fmt.Scanf("%d %s", &a, &b) 尝试按格式化字符串 %d %s 解析输入数据。
b.%d 匹配到整数部分,读取 123,成功解析并赋值给变量 a。
c.%s 匹配到字符串部分,读取 hello,成功解析并赋值给变量 b。
d.剩余的换行符 \n 被 Scanf 视为分隔符(空白字符)而忽略掉。
结果:
-
输出:
a: 123
b: hello
示例 2:输入 "123\nhello"
- 输入内容:123,按下 Enter 键(输入 "123\n"),然后再输入 hello 并按下 Enter。
- 执行过程:
a.fmt.Scanf("%d %s", &a, &b) 首先尝试解析 %d,从输入 "123\n" 中读取到整数 123 并赋值给 a。
b.接下来,fmt.Scanf 尝试读取 %s,但此时输入流中的字符是换行符 \n,它不符合字符串的格式要求。因此,Scanf 读取 %s 的尝试失败,换行符 \n 被保留在输入流中等待下一次读取。
c.因为读取 %s 失败,b 没有被赋值,程序继续等待下一个输入。
d.当用户再输入 hello 并按下 Enter 后,Scanf 成功读取到字符串 hello 并赋值给 b。
结果:
-
输出:
a: 123
b: hello
解释
在示例 1 中,"123 hello\n" 中的换行符被视为空白字符,符合格式化字符串的要求,因此被忽略。而在示例 2 中,"123\nhello" 中的换行符不符合 %s 的格式要求,导致 Scanf 读取失败,换行符保留在输入流中,等待下一次读取。
这展示了 fmt.Scanf 处理换行符的两种情况:符合格式时忽略,不符合时保留在输入流中。