若要输出消息至控制台,可以透过fmt
的Print
、Println
、Printf
等函数,如果要从控制台读取使用者输入,可以透过fmt
的Scanf
、Scanln
等函数。例如:
package main
import "fmt"
func main() {
fmt.Print("输入名称 年龄:")
var name string
var age int
fmt.Scanf("%s %d", &name, &age)
fmt.Printf("嗨!%s!今年 %d 岁了啊?", name, age)
}
%s
、%d
是格式符号,在 Go 中称为 verb,Go 可用的 verb 可以在fmt
包的文件中找到。
Scanf
就类似 C 语言中的scanf
,可以格式化地获取输入,底下是个范例:
输入名称 年龄:Justin 45
嗨!Justin!今年 45 岁了啊?
在按下 Enter 键后,实际上还有个 CR(carriage return)字符还未扫描,如果只是要获取空白分隔的输入,并以换行作为结束,可以使用Scanln
:
package main
import "fmt"
func main() {
fmt.Print("输入空白分隔的文本")
var text1, text2 string
fmt.Scanln(&text1, &text2)
fmt.Println(text1)
fmt.Println(text2)
}
如果是Scan
的话,也是扫描以空白区隔的输入,按下 Enter 键的 CR 字符,也会被视为空白。
Println
、Printf
会使用标准输出(Standout),如果想使用标准错误(Standard err)呢?可以透过Fprint
、Fprintln
、Fprintf
等函数,第一个实参指定os.Stderr
。例如:
package main
import (
"fmt"
"os"
)
func main() {
fmt.Fprintln(os.Stderr, "输出至标准错误")
}
os
包的Stderr
代表标准错误,而Stdin
、Stdout
代表标准输入与输出,它们的类型是*os.File
,若愿意的话,也可以直接操作它们,例如File
定义了Read
与Write
方法,可以指定一个类型为byte[]
的 slice,Read
会读入同样长度的数据至 slice,后者可以将同等长度的数据输出。例如:
package main
import "os"
func main() {
buf := make([]byte, 5);
os.Stdout.Write([]byte("输入五个数字:"))
os.Stdin.Read(buf)
os.Stdout.Write(buf)
}
实际上,os.File
可用的方法不只有Read
、Write
,先留意这两个方法的目的在于,这两个方法分别符合io.Reader
、io.Writer
定义的行为:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
如果察看fmt
的Fprint
、Fprintln
、Fprintf
等函数,可以发现它们第一个参数定义的类型并不是*os.File
,而是io.Writer
:
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
类似地,Fscan 字样开头的几个函数,第一个参数接受的是io.Reader
:
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)
这表示,fmt
包中这些函数,并不只能用于标准输入、输出或错误,例如,strings.NewReader
函数,可以指定字符串,返回*Reader
,这表示fmt
的Fscanf
等函数,可以从字符串读取输入。例如:
package main
import (
"fmt"
"io"
"strings"
)
func main() {
data := `Justin 45
Monica 42
Irene 12`
r := strings.NewReader(data)
var name string
var age int
for {
if _, err := fmt.Fscanln(r, &name, &age); err == io.EOF {
break
}
fmt.Printf("%s: %d\n", name, age)
}
}
Fscanln
会返回扫描的笔数,如果笔数少于指定的扫描数量,err
会指出原因,在文件读取结束(End of file)时,err
会是io.EOF
,在上例中,数据来源是个格式确定的字符串,因此仅简单地判断err
是否为io.EOF
来结束扫描。
os.File
不过是具有io.Reader
、io.Writer
的行为罢了,os.File
代表文件,也就是说Fprint
、Fprintln
、Fprintf
、Fscan
、Fscanln
、Fscanf
等函数,也可以用在文件读写,其实标准输入、输出、错误等,也是被视为文件的,这在os
的file.go可以看到:
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
因此 IO 之类的操作,在 Go 中非常灵活,一切都看 API 上可接受行为而定,不受类型之限制,这之后再从实际的例子中来谈。