Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

My Blog

从此烟雨落金城,一人撑伞两人行。

输入输出

io-基本的IO接口

io包为I/O原语提供了基本的接口。包装了原语的已有实现。

只要满足Reader和Writer接口,就可以使用IO包的功能。

Reader接口

定义:

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}

Read将len(p)个字节读取到p中。返回读取的字节数n和遇到的错误。

Reader接口的方法集只包含了一个Read方法,所有实现了Read方法的类型都可以满足io.Reader接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
}
// 从标准输入读取
data, err = ReadFrom(os.Stdin, 11)

// 从普通文件读取,其中 file 是 os.File 的实例
data, err = ReadFrom(file, 9)

// 从字符串读取
data, err = ReadFrom(strings.NewReader("from string"), 12)

传递的参数是个接口,在函数中调用了这个接口变量的Read方法,只需要在向ReadFrom函数传递实参的时候传递一个实现了该接口的对象,这样就把对象赋值给了接口。

Writer 接口

定义:

1
2
3
type Write interface {
Write(p byte[]) (n int, err error)
}

所有实现了Write方法的类型都实现了 io.Writer 接口。

实现了io.Reader接口或io.Writer接口的类型

  • os.File 同时实现了 io.Reader 和 io.Writer
  • strings.Reader 实现了 io.Reader
  • bufio.Reader/Writer 分别实现了 io.Reader 和 io.Writer
  • bytes.Buffer 同时实现了 io.Reader 和 io.Writer
  • bytes.Reader 实现了 io.Reader
  • encoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer
  • net/conn 分别实现了 io.Reader 和 io.Writer(Conn接口定义了Read/Write)

常用的类型有:os.File、strings.Reader、bufio.Reader/Writer、bytes.Buffer、bytes.Reader

Go 中接口的命名约定:接口名以 er 结尾。

ReaderAt 和 WriterAt 接口

ReaderAt 接口的定义:

1
2
3
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}

ReadAt 从基本输入源的偏移量 off 处开始,将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p))以及任何遇到的错误。

1
2
3
4
5
6
7
8
9
reader := strings.NewReader("Go语言中文网")
p := make([]byte, 6)
n, err := reader.ReadAt(p, 2)
if err != nil {
panic(err)
}
fmt.Printf("%s, %d\n", p, n)

// 语言, 6

WriterAt 接口的定义:

1
2
3
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}

WriteAt 从 p 中将 len(p) 个字节写入到偏移量 off 处的基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。

1
2
3
4
5
6
7
8
9
10
11
file, err := os.Create("writeAt.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Golang中文社区——这里是多余")
n, err := file.WriteAt([]byte("Go语言中文网"), 24)
if err != nil {
panic(err)
}
fmt.Println(n)

ReaderFrom 和 WriterTo 接口

ReaderFrom 的定义:

1
2
3
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}

ReadFrom 从 r 中读取数据,直到 EOF 或发生错误。其返回值 n 为读取的字节数。

1
2
3
4
5
6
7
8
file, err := os.Open("writeAt.txt")
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(os.Stdout)
writer.ReadFrom(file)
writer.Flush()

WriterTo的定义:

1
2
3
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}

WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数。

1
2
reader := bytes.NewReader([]byte("Go语言中文网"))
reader.WriteTo(os.Stdout)

ioutil — 方便的IO操作函数集

ReadAll 函数

Go 提供了 ReadAll 这个函数,用来从io.Reader 中一次读取所有数据。

1
func ReadAll(r io.Reader) ([]byte, error)

它是通过 bytes.Buffer 中的 ReadFrom 来实现读取所有数据的。

ReadDri 函数

读取目录并返回排好序的文件和子目录名( []os.FileInfo )。

1
fileInfos, err := ioutil.ReadDir(path)

ReadFile 和 WriteFile 函数

ReadFile 读取整个文件的内容。

ReadFile 的实现和ReadAll 类似,不过,ReadFile 会先判断文件的大小,给 bytes.Buffer 一个预定义容量,避免额外分配内存。

1
func ReadFile(filename string) ([]byte, error)

ReadFile 从 filename 指定的文件中读取数据并返回文件的内容。

WriteFile 函数:

1
func WriteFile(filename string, data []byte, perm os.FileMode) error

WriteFile 将data写入filename文件中,当文件不存在时会根据perm指定的权限进行创建一个,文件存在时会先清空文件内容。

TempDir 和 TempFile 函数

TempDir 创建临时目录。

1
b.work, err = ioutil.TempDir("", "go-build")

第一个参数如果为空,表明在系统默认的临时目录( os.TempDir )中创建临时目录;第二个参数指定临时目录名的前缀,该函数返回临时目录的路径。

TempFile 用于创建临时文件。

1
f1, err := ioutil.TempFile("", "gofmt")

第一个参数如果为空,表明在系统默认的临时目录( os.TempDir )中创建临时文件;第二个参数指定临时文件名的前缀,该函数返回临时文件的路径。

创建者创建的临时文件和临时目录要负责删除这些临时目录和文件。

如删除临时文件:

1
2
3
4
defer func() {
f.Close()
os.Remove(f.Name())
}()

fmt — 格式化IO

Printing

Sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
type user struct {
name string
}

func main() {
u := user{"tang"}
//Printf 格式化输出
fmt.Printf("% + v\n", u) //格式化输出结构
fmt.Printf("%#v\n", u) //输出值的 Go 语言表示方法
fmt.Printf("%T\n", u) //输出值的类型的 Go 语言表示
fmt.Printf("%t\n", true) //输出值的 true 或 false
fmt.Printf("%b\n", 1024) //二进制表示
fmt.Printf("%c\n", 11111111) //数值对应的 Unicode 编码字符
fmt.Printf("%d\n", 10) //十进制表示
fmt.Printf("%o\n", 8) //八进制表示
fmt.Printf("%q\n", 22) //转化为十六进制并附上单引号
fmt.Printf("%x\n", 1223) //十六进制表示,用a-f表示
fmt.Printf("%X\n", 1223) //十六进制表示,用A-F表示
fmt.Printf("%U\n", 1233) //Unicode表示
fmt.Printf("%b\n", 12.34) //无小数部分,两位指数的科学计数法6946802425218990p-49
fmt.Printf("%e\n", 12.345) //科学计数法,e表示
fmt.Printf("%E\n", 12.34455) //科学计数法,E表示
fmt.Printf("%f\n", 12.3456) //有小数部分,无指数部分
fmt.Printf("%g\n", 12.3456) //根据实际情况采用%e或%f输出
fmt.Printf("%G\n", 12.3456) //根据实际情况采用%E或%f输出
fmt.Printf("%s\n", "wqdew") //直接输出字符串或者[]byte
fmt.Printf("%q\n", "dedede") //双引号括起来的字符串
fmt.Printf("%x\n", "abczxc") //每个字节用两字节十六进制表示,a-f表示
fmt.Printf("%X\n", "asdzxc") //每个字节用两字节十六进制表示,A-F表示
fmt.Printf("%p\n", 0x123) //0x开头的十六进制数表示
}

占位符

占位符 说明
%v 相应值的默认格式。“加号”标记(%+v)会添加字段名
%#v 相应值的Go语法表示
%T 相应值的类型的Go语法表示
%% 字面上的百分号,并非值的占位符
%t true 或 false。
%b 二进制表示
%c 相应Unicode码点所表示的字符
%d 十进制表示
%o 八进制表示
%q 单引号围绕的字符字面值,由Go语法安全地转义
%x 十六进制表示,字母形式为小写 a-f
%X 十六进制表示,字母形式为大写 A-F
%s 输出字符串表示(string类型或[]byte)
%p 十六进制表示,前缀 0x
%f 有小数点而无指数,例如 123.456
%e 科学计数法,例如 -1234.456e+78
%g 根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出
%G 根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出
%E 科学计数法,例如 -1234.456E+78

对数值而言,宽度为该数值占用区域的最小宽度;精度为小数点之后的位数。

Scanning

Scan、Scanf 和 Scanln 从 os.Stdin 中读取; Fscan、Fscanf 和 Fscanln 从指定的 io.Reader 中读取; Sscan、Sscanf 和 Sscanln 从实参字符串中读取。

Scanf、Fscanf 和 Sscanf 需要输入换行符来匹配格式中的换行符;其它函数则将换行符视为空格。

Scanf、Fscanf 和 Sscanf 根据格式字符串解析实参。

宽度被解释为输入的文本(%5s 意为最多从输入中读取5个 rune 来扫描成字符串),而扫描函数则没有精度的语法(没有 %5.2f,只有 %5f)。

Fprint/Fprintf/Fprintln 函数的第一个参数接收一个io.Writer类型,会将内容输出到 io.Writer 中去。

Print/Printf/Println 函数是将内容输出到标准输出中。

Sprint/Sprintf/Sprintln 是格式化内容为 string 类型,而并不输出到某处,需要格式化字符串并返回时。

Scan 序列函数

Fscan/Fscanf/Fscanln 函数的第一个参数接收一个 io.Reader 类型,从其读取内容并赋值给相应的实参。

Scan/Scanf/Scanln 正是从标准输入获取内容。

Sscan/Sscanf/Sscanln 则直接从字符串中获取内容。

bufio — 缓存IO

Reader 类型和方法

bufio.Reader 结构包装了一个 io.Reader 对象,提供缓存功能,同时实现了 io.Reader 接口。

1
2
3
4
5
6
7
8
9
10
type Reader struct {
buf []byte // 缓存
rd io.Reader // 底层的io.Reader
// r:从buf中读走的字节(偏移);w:buf中填充内容的偏移;
// w - r 是buf中可被读的长度(缓存数据的大小),也是Buffered()方法的返回值
r, w int
err error // 读过程中遇到的错误
lastByte int // 最后一次读到的字节(ReadByte/UnreadByte)
lastRuneSize int // 最后一次读到的Rune的大小 (ReadRune/UnreadRune)
}

实列化

bufio 包提供了两个实例化 bufio.Reader 对象的函数:NewReader 和 NewReaderSize。其中,NewReader 函数是调用 NewReaderSize 函数实现的:

1
2
3
4
func NewReader(rd io.Reader) *Reader {
// 默认缓存大小:defaultBufSize=4096
return NewReaderSize(rd, defaultBufSize)
}

NewReaderSize的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func NewReaderSize(rd io.Reader, size int) *Reader {
// 已经是bufio.Reader类型,且缓存大小不小于 size,则直接返回
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
// 缓存大小不会小于 minReadBufferSize (16字节)
if size < minReadBufferSize {
size = minReadBufferSize
}
// 构造一个bufio.Reader实例
return &Reader{
buf: make([]byte, size),
rd: rd,
lastByte: -1,
lastRuneSize: -1,
}
}

评论