输入输出
输入输出
package main
import "fmt"
func main() {
fmt.Println("Hello 世界!")
}
本站的第一个入门的案例就是输出一个字符串,这一节就来讲一下在Go中如何进行输入输出。
文件描述符
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
在os
包下有三个外暴露的文件描述符,其类型都是*os.File
,分别是:
os.Stdin
- 标准输入os.Stdout
- 标准输出os.Stderr
- 标准错误
Go中的输入输出都离不开它们。
输出
在Go中输出有很多中方法,下面几个比较常见的
stdout
因为标准输出本身就是一个文件,所以你可以直接将字符串写入到标准输出中
package main
import "os"
func main() {
os.Stdout.WriteString("hello world!")
}
Go有两个内置的函数print
,println
,他们会将参数输出到标准错误中,仅做调试用,一般不推荐使用。
package main
func main() {
print("hello world!\n")
println("hello world")
}
fmt
最常见的用法是使用fmt
包,它提供了fmt.Println
函数,该函数默认会将参数输出到标准输出中。
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}
它的参数支持任意类型,如果类型实现了String
接口也会调用String
方法来获取其字符串表现形式,所以它输出的内容可读性比较高,适用于大部分情况,不过由于内部用到了反射,在性能敏感的场景不建议大量使用。
bufio
bufio
提供了可缓冲的输出方法,它会先将数据写入到内存中,积累到了一定阈值再输出到指定的Writer
中,默认缓冲区大小是4KB
。在文件IO,网络IO的时候建议使用这个包。
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
writer.WriteString("hello world!")
}
你也可以把它和fmt
包结合起来用
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
fmt.Fprintln(writer, "hello world!")
}
格式化
Go中的格式化输出功能基本上由fmt.Printf
函数提供,如果你学过C系语言,一定会觉得很熟悉,下面是一个简单的例子。
func main() {
fmt.Printf("hello world, %s!", "jack")
}
下面是Go目前所有的格式化动词。
0 | 格式化 | 描述 | 接收类型 |
---|---|---|---|
1 | %% | 输出百分号% | 任意 |
2 | %s | 输出string /[] byte 值 | string ,[] byte |
3 | %q | 格式化字符串,输出的字符串两端有双引号"" | string ,[] byte |
4 | %d | 输出十进制整型值 | 整型 |
5 | %f | 输出浮点数 | 浮点 |
6 | %e | 输出科学计数法形式 ,也可以用于复数 | 浮点 |
7 | %E | 与%e 相同 | 浮点 |
8 | %g | 根据实际情况判断输出%f 或者%e ,会去掉多余的0 | 浮点 |
9 | %b | 输出整型的二进制表现形式 | 数字 |
10 | %#b | 输出二进制完整的表现形式 | 数字 |
11 | %o | 输出整型的八进制表示 | 整型 |
12 | %#o | 输出整型的完整八进制表示 | 整型 |
13 | %x | 输出整型的小写十六进制表示 | 数字 |
14 | %#x | 输出整型的完整小写十六进制表示 | 数字 |
15 | %X | 输出整型的大写十六进制表示 | 数字 |
16 | %#X | 输出整型的完整大写十六进制表示 | 数字 |
17 | %v | 输出值原本的形式,多用于数据结构的输出 | 任意 |
18 | %+v | 输出结构体时将加上字段名 | 任意 |
19 | %#v | 输出完整Go语法格式的值 | 任意 |
20 | %t | 输出布尔值 | 布尔 |
21 | %T | 输出值对应的Go语言类型值 | 任意 |
22 | %c | 输出Unicode码对应的字符 | int32 |
23 | %U | 输出字符对应的Unicode码 | rune ,byte |
24 | %p | 输出指针所指向的地址 | 指针 |
使用fmt.Sprintf
或者fmt.Printf
来格式化字符串或者输出格式化字符串,看几个例子
fmt.Printf("%%%s\n", "hello world")
fmt.Printf("%s\n", "hello world")
fmt.Printf("%q\n", "hello world")
fmt.Printf("%d\n", 2<<7-1)
fmt.Printf("%f\n", 1e2)
fmt.Printf("%e\n", 1e2)
fmt.Printf("%E\n", 1e2)
fmt.Printf("%g\n", 1e2)
fmt.Printf("%b\n", 2<<7-1)
fmt.Printf("%#b\n", 2<<7-1)
fmt.Printf("%o\n", 2<<7-1)
fmt.Printf("%#o\n", 2<<7-1)
fmt.Printf("%x\n", 2<<7-1)
fmt.Printf("%#x\n", 2<<7-1)
fmt.Printf("%X\n", 2<<7-1)
fmt.Printf("%#X\n", 2<<7-1)
type person struct {
name string
age int
address string
}
fmt.Printf("%v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%+v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%#v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%t\n", true)
fmt.Printf("%T\n", person{})
fmt.Printf("%c%c\n", 20050, 20051)
fmt.Printf("%U\n", '码')
fmt.Printf("%p\n", &person{})
使用其它进制时,在%
与格式化动词之间加上一个空格便可以达到分隔符的效果,例如
func main() {
str := "abcdefg"
fmt.Printf("%x\n", str)
fmt.Printf("% x\n", str)
}
该例输出的结果为
61626364656667
61 62 63 64 65 66 67
在使用数字时,还可以自动补零。比如
fmt.Printf("%09d", 1)
// 000000001
二进制同理
fmt.Printf("%09b", 1<<3)
// 000001000
错误情况
格式化字符数量 < 参数列表数量
fmt.Printf("", "") //%!(EXTRA string=)
格式化字符数量 > 参数列表数量
fmt.Printf("%s%s", "") //%!s(MISSING)
类型不匹配
fmt.Printf("%s", 1) //%!s(int=1)
缺少格式化动词
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)
输入
下面介绍常见的输入方法
read
你可以像直接读文件一样,读取输入内容,如下
func main() {
var buf [1024]byte
n, _ := os.Stdin.Read(buf[:])
os.Stdout.Write(buf[:n])
}
这样用起来太麻烦了,一般不推荐使用。
fmt
我们可以使用fmt
包提供的几个函数,用起来跟C差不多。
// 扫描从os.Stdin读入的文本,根据空格分隔,换行也被当作空格
func Scan(a ...any) (n int, err error)
// 与Scan类似,但是遇到换行停止扫描
func Scanln(a ...any) (n int, err error)
// 根据格式化的字符串扫描
func Scanf(format string, a ...any) (n int, err error)
读取两个数字
func main() {
var a, b int
fmt.Scanln(&a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}
读取固定长度的数组
func main() {
n := 10
s := make([]int, n)
for i := range n {
fmt.Scan(&s[i])
}
fmt.Println(s)
}
1 2 3 4 5 6 7 8 9 10
[1 2 3 4 5 6 7 8 9 10]
bufio
在有大量输入需要读取的时候,就建议使用bufio.Reader
来进行内容读取
func main() {
reader := bufio.NewReader(os.Stdin)
var a, b int
fmt.Fscanln(reader, &a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}
scanner
bufio.Scanner
与bufio.Reader
类似,不过它是按行读取的。
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "exit" {
break
}
fmt.Println("scan", line)
}
}
结果如下
first line
scan first line
second line
scan second line
third line
scan third line
exit
提示
在输入输出这方面,想要练手的话,去洛谷做几道简单的ACM模式算法题就能上手熟悉了。