1 IO包
Go 语言的 “io” 标准库包是一个用于处理 I/O(输入/输出)操作的库。它提供了一组接口和实现,用于从不同来源读取和写入数据。这个包的核心概念是 Reader 和 Writer 接口,它们分别用于从不同来源读取数据和将数据写入不同的目标
1.1 Reader 接口
定义了用于读取数据的基本方法。最重要的方法是 Read,它接受一个字节切片作为参数,并将数据填充到这个切片中。Read 方法返回读取的字节数和可能遇到的错误。
type Reader interface {
Read(p []byte) (n int, err error)
}
用法:
func ReadFrom(reader io.Reader, num int) ([]byte, error) {
p := make([]byte, num)
n, err := reader.Read(p)
if n > 0 {
return p[:n], nil
}
return p, err
}
1.2 Writer 接口
定义了用于写入数据的基本方法。最重要的方法是 Write,它接受一个字节切片作为参数,并将数据写入目标。Write 方法返回写入的字节数和可能遇到的错误
type Writer interface {
Write(p []byte) (n int, err error)
}
1.3 ReaderAt 和 WriterAt 接口
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
1.4 ReaderFrom 和 WriterTo 接口
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
2 io/ioutil包 (1.16后废弃)
2.1 ReadAll
用于从 io.Reader 中读取所有数据到字节切片。在 Go 1.16 之后,请使用 “io” 包中的 “io.ReadAll” 函数代替。
// ioutil.ReadAll
func ReadAll(r io.Reader) ([]byte, error)
// io.ReadAll (Go 1.16+)
func ReadAll(r io.Reader) ([]byte, error)
2.2 ReadFile
用于一次性读取整个文件内容到字节切片。在 Go 1.16 之后,请使用 “os” 包中的 “os.ReadFile” 函数代替。
// ioutil.ReadFile
func ReadFile(filename string) ([]byte, error)
// os.ReadFile (Go 1.16+)
func ReadFile(name string) ([]byte, error)
2.3 WriteFile
用于一次性将字节切片写入文件。在 Go 1.16 之后,请使用 “os” 包中的 “os.WriteFile” 函数代替。
// ioutil.WriteFile
func WriteFile(filename string, data []byte, perm os.FileMode) error
// os.WriteFile (Go 1.16+)
func WriteFile(name string, data []byte, perm FileMode) error
2.4 TempFile
用于在指定目录创建一个临时文件。在 Go 1.16 之后,请使用 “os” 包中的 “os.CreateTemp” 函数代替。
// ioutil.TempFile
func TempFile(dir, pattern string) (f *os.File, err error)
// os.CreateTemp (Go 1.16+)
func CreateTemp(dir, pattern string) (*File, error)
2.5 TempDir
用于在指定目录创建一个临时目录。在 Go 1.16 之后,请使用 “os” 包中的 “os.MkdirTemp” 函数代替。
// ioutil.TempDir
func TempDir(dir, pattern string) (name string, err error)
// os.MkdirTemp (Go 1.16+)
func MkdirTemp(dir, pattern string) (string, error)
3 fmt包
3.1 Printing
%d 十进制表示
%f 有小数点而无指数,例如 123.456
%s 输出字符串表示(string类型或[]byte)
%p 十六进制表示,前缀 0x
%t 单词 true 或 false。
3.2 Scanning
%p 没有实现
%T 没有实现
%d 十进制表示
%f 有小数点而无指数,例如 123.456
%s 输出字符串表示(string类型或[]byte)
%p 十六进制表示,前缀 0x
%t 单词 true 或 false。
3.3 Print 序列函数
Fprint/Fprintf/Fprintln/Sprint/Sprintf/Sprintln/Print/Printf/Println
一般的,我们将 Fprint/Fprintf/Fprintln 归为一类;Sprint/Sprintf/Sprintln 归为一类;Print/Printf/Println 归为另一类。
- Fprint/Fprintf/Fprintln 函数的第一个参数接收一个io.Writer类型,会将内容输出到 io.Writer 中去
- Sprint/Sprintf/Sprintln 是格式化内容为 string 类型,而并不输出到某处,需要格式化字符串并返回时,可以用这组函数
- Print/Printf/Println 函数是将内容输出到标准输出中
其中,Print/Printf/Println 会调用相应的F开头一类函数,并将 os.Stdout 作为第一个参数传入。如:
func Print(a ...interface{}) (n int, err error) {
return Fprint(os.Stdout, a...)
}
3.4 Stringer 接口
Stringer接口的定义如下:
type Stringer interface {
String() string
}
根据 Go 语言中实现接口的定义,一个类型只要有 String() string 方法,我们就说它实现了 Stringer 接口。而在本节开始已经说到,如果格式化输出某种类型的值,只要它实现了 String() 方法,那么会调用 String() 方法进行处理。
接下来,为Person增加String方法。
func (this *Person) String() string {
buffer := bytes.NewBufferString("This is ")
buffer.WriteString(this.Name + ", ")
if this.Sex == 0 {
buffer.WriteString("He ")
} else {
buffer.WriteString("She ")
}
buffer.WriteString("is ")
buffer.WriteString(strconv.Itoa(this.Age))
buffer.WriteString(" years old.")
return buffer.String()
}
type Person struct {
Name string
Age int
Sex int
}
p := &Person{"polaris", 28, 0}
fmt.Println(p)
3.5 Formatter 接口
Formatter 接口的定义如下:
type Formatter interface {
Format(f State, c rune)
}
Formatter 接口由带有定制的格式化器的值所实现。 Format 的实现可调用Printf/ Sprintf / Fprintf(f) 等函数来生成其输出。
具体实例如下
func (this *Person) Format(f fmt.State, c rune) {
if c == 'L' {
f.Write([]byte(this.String()))
f.Write([]byte(" Person has three fields."))
} else {
// 没有此句,会导致 fmt.Printf("%s", p) 啥也不输出
f.Write([]byte(fmt.Sprintln(this.String())))
}
}
p := &Person{"polaris", 28, 0}
fmt.Printf("%L", p)
3.6 GoStringer 接口
GoStringer 接口定义如下;
type GoStringer interface {
GoString() string
}
该接口定义了类型的Go语法格式。用于打印(Printf)格式化占位符为 %#v 的值。
func (this *Person) GoString() string {
return "&Person{Name is "+this.Name+", Age is "+strconv.Itoa(this.Age)+", Sex is "+strconv.Itoa(this.Sex)+"}"
}
p := &Person{"polaris", 28, 0}
fmt.Printf("%#v", p)
3.7 Scan 序列函数
Fscan/Fscanf/Fscanln/Sscan/Sscanf/Sscanln/Scan/Scanf/Scanln。
一般的,我们将Fscan/Fscanf/Fscanln归为一类;Sscan/Sscanf/Sscanln归为一类;Scan/Scanf/Scanln归为另一类。
Fscan/Fscanf/Fscanln 函数的第一个参数接收一个 io.Reader 类型,从其读取内容并赋值给相应的实参
Sscan/Sscanf/Sscanln 则直接从字符串中获取内容。
Scan/Scanf/Scanln 正是从标准输入获取内容
其中,Scan/Scanf/Scanln会调用相应的F开头一类函数,并将 os.Stdin 作为第一个参数传入。如:
func Scan(a ...interface{}) (n int, err error) {
return Fscan(os.Stdin, a...)
}
Scan/FScan/Sscan
var (
name string
age int
)
n, _ := fmt.Sscan("polaris 28", &name, &age)
// 可以将"polaris 28"中的空格换成"\n"试试
// n, _ := fmt.Sscan("polaris\n28", &name, &age)
fmt.Println(n, name, age)
不管"polaris 28"是用空格分隔还是"\n"分隔,输出一样。也就是说,Scan/FScan/Sscan
这组函数将连续由空格分隔的值存储为连续的实参(换行符也记为空格)。
Scanf/FScanf/Sscanf
var (
name string
age int
)
n, _ := fmt.Sscanf("polaris 28", "%s%d", &name, &age)
// 可以将"polaris 28"中的空格换成"\n"试试
// n, _ := fmt.Sscanf("polaris\n28", "%s%d", &name, &age)
fmt.Println(n, name, age)
如果将"空格"分隔改为"\n"分隔,则输出为:1 polaris 0。可见,Scanf/FScanf/Sscanf
这组函数将连续由空格分隔的值存储为连续的实参, 其格式由 format
决定,换行符处停止扫描(Scan)。
Scanln/FScanln/Sscanln
var (
name string
age int
)
n, _ := fmt.Sscanln("polaris 28", &name, &age)
// 可以将"polaris 28"中的空格换成"\n"试试
// n, _ := fmt.Sscanln("polaris\n28", &name, &age)
fmt.Println(n, name, age)
Scanln/FScanln/Sscanln
表现和上一组一样,遇到"\n"停止(对于Scanln,表示从标准输入获取内容,最后需要回车)。
一般地,我们使用 Scan/Scanf/Scanln
这组函数。