Go 学习笔记(二十二)读取标准输入
本文原创地址:博客园骏马金龙Go 基础系列:读取标准输入
fmt 包中提供了 3 类读取输入的函数:
- Scan 家族:从标准输入 os.Stdin 中读取数据,包括 Scan()、Scanf()、Scanln()
- SScan 家族:从字符串中读取数据,包括 Sscan()、Sscanf()、Sscanln()
- Fscan 家族:从 io.Reader 中读取数据,包括 Fscan()、Fscanf()、Fscanln()
其中:
- Scanln、Sscanln、Fscanln 在遇到换行符的时候停止
- Scan、Sscan、Fscan 将换行符当作空格处理
- Scanf、Sscanf、Fscanf 根据给定的 format 格式读取,就像 Printf 一样
这 3 家族的函数都返回读取的记录数量,并会设置报错信息,例如读取的记录数量不足、超出或者类型转换失败等。
以下是它们的定义方式:
$ go doc fmt | grep -Ei "func [FS]*Scan"
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Scan(a ...interface{}) (n int, err error)
func Scanf(format string, a ...interface{}) (n int, err error)
func Scanln(a ...interface{}) (n int, err error)
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
因为还没介绍 io.Reader,所以 Fscan 家族的函数暂且略过,但用法和另外两家族的 scan 类函数是一样的。
Scan、Scanf 和 Scanln
Scan 家族函数从标准输入读取数据时,将以空格为分隔符分隔标准输入中的内容,并将分隔后的各个记录保存到给定的变量中。其中 Scanf() 可以指定分隔符。
例如,使用 Scanln 函数等待用户输入数据,或从管道中读取数据。下面的代码将等待用户输入,且将读取的内容分别保存到 name 变量和 age 变量中:
package main
import (
"fmt"
)
func main() {
var (
name string
age int
)
fmt.Print("输入姓名和年龄,使用空格分隔:")
fmt.Scanln(&name, &age)
fmt.Printf("name: %s\nage: %d\n", name, age)
}
因为 Scanln() 遇到换行符或 EOF 的时候终止读取,所以在输入的时候只要按下回车键就会结束读取。
运行它,将提示输入姓名:
请输入姓名和年龄,空格分隔:
周伯通 69
name: 周伯通
age: 69
同理 Scanf()也在遇到换行符或 EOF 的时候终止读取行为。使用 Scanf() 的时候,需要给定格式化字符串形式:
例如:
fmt.Scanf("%s %d",&name,&age)
输入时,第一个字段会转换成字符串格式保存到name
变量中,第二个记录会转换成整数保存到age
中,如果转换失败,将不会进行保存。例如输入malongshuai aaa
,由于aaa
无法转换成 int,所以 age 变量的值仍然为初始化的数值 0。
Scanf 可指定分隔符,其中上面的是%s %d
中间的空格就是分隔符。例如下面指定:
作为分隔符:
fmt.Scanf("%s : %d",&name,&age)
在输入时,必须按照以下格式进行输入:首先至少一个空格,然后一个冒号,再至少一个空格:
周伯通 : 23 // 或者连续多个空格 "周伯通 : 23"
name: 周伯通
age: 23
如果使用的是fmt.Scan()
,则输入数据时可以换行输入,Scan() 会将换行符作为空格进行处理,直到读取到了 2 个记录之后自动终止读取操作:
fmt.Scan(&name, &age)
请输入姓名和年龄,空格分隔:周伯通
87
name: 周伯通
age: 87
一般来说,只使用 Scanf 类函数比较好。
返回值
这些函数都有返回值:读取的记录数量和 err 信息。
以 Scanln() 为例:
func main() {
var (
name string
age int
)
fmt.Print("输入姓名和年龄,使用空格分隔:")
n, err := fmt.Scanln(&name, &age)
fmt.Printf("name: %s\nage: %d\n", name, age)
fmt.Println("records count:",n)
fmt.Println("err or not:",err)
}
输入:
malongshuai 23 // n = 2, err = nil
malongshuai // n = 1, err != nil
malongshuai long // n = 2, err != nil
malongshuai 23 23 // n = 2, err != nil
Sscan、Sscanf 和 Scanln
Sscan 家族的函数用于从给定字符串中读取数据,用法和 Scan 家族类似。
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
例如:
package main
import (
"fmt"
)
func main() {
var (
name string
age int
)
input := "malongshuai 23"
fmt.Sscan(input, &name, &age)
fmt.Printf("name: %s\nage: %d\n", name, age)
}
使用 Sscanf() 可以指定分隔符:
input := "malongshuai : 23"
fmt.Sscanf(input, "%s : %d", &name, &age)
使用 bufio 中读取标准输入
除了 fmt 包的 Scan 类函数,bufio 包也可以读取标准输入。当然,读取标准输入只是它的一个功能示例,它的作用是操作缓冲 IO。
package main
import (
"bufio"
"fmt"
"os"
)
var inputReader *bufio.Reader
var input string
var err error
func main() {
inputReader = bufio.NewReader(os.Stdin)
fmt.Println("输入姓名:")
input, err = inputReader.ReadString('\n')
if err == nil {
fmt.Printf("The input was: %s\n", input)
}
}
其中 NewReader() 创建一个 bufio.Reader 实例,表示创建一个从给定文件中读取数据的读取器对象。然后调用读取器对象 (Reader 实例) 的 ReadString()方法,这个方法以\n
作为分隔符,它的分隔符必须只能是单字符,且必须使用单引号包围,因为它会作为 byte 读取。ReadString()读取来自 os.Stdin 的内容后将其保存到 input 变量中,同时返回是否出错的信息。ReadString() 只有一种情况会返回 err:没有遇到分隔符。
ReadString 会将读取的内容 包括分隔符 都一起放进缓冲中,如果读取文件时读到了结尾,则会将整个文件内容放进缓冲,并将文件终止标识符 io.EOF 放进设置为 err。
通常无需单独定义这些变量,下面是更常见的用法。
inputReader := bufio.NewReader(os.Stdin)
input, err := inputReader.ReadString('\n')
上一篇 Go 学习笔记(二十一)接口类型断言和 type-switch
Go 学习笔记(目录)
下一篇 Go 学习笔记(二十三)WaitGroup 用法说明