• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

GO 标准库 学习记录

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

Go标准库线上学习网站(带例子)

https://books.studygolang.com/The-Golang-Standard-Library-by-Example/

 

bufio包

 

Go语言在io操作中,还提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率。

bufio 包介绍 

bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

以上为官方包的介绍,在其中我们能了解到的信息如下:

bufio 是通过缓冲来提高效率

简单的说就是,把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io 从而提高速度。同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。看完以上解释有人可能会表示困惑了,直接把 内容->文件 和 内容->缓冲->文件相比, 缓冲区好像没有起到作用嘛。其实缓冲区的设计是为了存储多次的写入,最后一口气把缓冲区内容写入文件。下面会详细解释

bufio 封装了io.Reader或io.Writer接口对象,并创建另一个也实现了该接口的对象

io.Reader或io.Writer 接口实现read() 和 write() 方法,对于实现这个接口的对象都是可以使用这两个方法的

bufio 包实现原理

 

bufio 源码分析

Reader对象

bufio.Reader 是bufio中对io.Reader 的封装

?

1

2

3

4

5

6

7

8

9

// Reader implements buffering for an io.Reader object.

type Reader struct {

  buf     []byte

  rd      io.Reader // reader provided by the client

  r, w     int    // buf read and write positions

  err     error

  lastByte   int

  lastRuneSize int

}

bufio.Read(p []byte) 相当于读取大小len(p)的内容,思路如下:

  1. 当缓存区有内容的时,将缓存区内容全部填入p并清空缓存区
  2. 当缓存区没有内容的时候且len(p)>len(buf),即要读取的内容比缓存区还要大,直接去文件读取即可
  3. 当缓存区没有内容的时候且len(p)<len(buf),即要读取的内容比缓存区小,缓存区从文件读取内容充满缓存区,并将p填满(此时缓存区有剩余内容)
  4. 以后再次读取时缓存区有内容,将缓存区内容全部填入p并清空缓存区(此时和情况1一样)

以下是源码

?

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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

// Read reads data into p.

// It returns the number of bytes read into p.

// The bytes are taken from at most one Read on the underlying Reader,

// hence n may be less than len(p).

// At EOF, the count will be zero and err will be io.EOF.

func (b *Reader) Read(p []byte) (n int, err error) {

  n = len(p)

  if n == 0 {

    return 0, b.readErr()

  }

  if b.r == b.w {

    if b.err != nil {

      return 0, b.readErr()

    }

    if len(p) >= len(b.buf) {

      // Large read, empty buffer.

      // Read directly into p to avoid copy.

      n, b.err = b.rd.Read(p)

      if n < 0 {

        panic(errNegativeRead)

      }

      if n > 0 {

        b.lastByte = int(p[n-1])

        b.lastRuneSize = -1

      }

      return n, b.readErr()

    }

    // One read.

    // Do not use b.fill, which will loop.

    b.r = 0

    b.w = 0

    n, b.err = b.rd.Read(b.buf)

    if n < 0 {

      panic(errNegativeRead)

    }

    if n == 0 {

      return 0, b.readErr()

    }

    b.w += n

  }

 

  // copy as much as we can

  n = copy(p, b.buf[b.r:b.w])

  b.r += n

  b.lastByte = int(b.buf[b.r-1])

  b.lastRuneSize = -1

  return n, nil

}

说明:

reader内部通过维护一个r, w 即读入和写入的位置索引来判断是否缓存区内容被全部读出

Writer对象

bufio.Writer 是bufio中对io.Writer 的封装

?

1

2

3

4

5

6

7

// Writer implements buffering for an io.Writer object.

type Writer struct {

  err error

  buf []byte

  n  int

  wr io.Writer

}

bufio.Write(p []byte) 的思路如下

  1. 判断buf中可用容量是否可以放下 p
  2. 如果能放下,直接把p拼接到buf后面,即把内容放到缓冲区
  3. 如果缓冲区的可用容量不足以放下,且此时缓冲区是空的,直接把p写入文件即可
  4. 如果缓冲区的可用容量不足以放下,且此时缓冲区有内容,则用p把缓冲区填满,把缓冲区所有内容写入文件,并清空缓冲区
  5. 判断p的剩余内容大小能否放到缓冲区,如果能放下(此时和步骤1情况一样)则把内容放到缓冲区
  6. 如果p的剩余内容依旧大于缓冲区,(注意此时缓冲区是空的,情况和步骤2一样)则把p的剩余内容直接写入文件

以下是源码

?

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

// Write writes the contents of p into the buffer.

// It returns the number of bytes written.

// If nn < len(p), it also returns an error explaining

// why the write is short.

func (b *Writer) Write(p []byte) (nn int, err error) {

  for len(p) > b.Available() && b.err == nil {

    var n int

    if b.Buffered() == 0 {

      // Large write, empty buffer.

      // Write directly from p to avoid copy.

      n, b.err = b.wr.Write(p)

    } else {

      n = copy(b.buf[b.n:], p)

      b.n += n

      b.flush()

    }

    nn += n

    p = p[n:]

  }

  if b.err != nil {

    return nn, b.err

  }

  n := copy(b.buf[b.n:], p)

  b.n += n

  nn += n

  return nn, nil

}

说明:

b.wr 存储的是一个io.writer对象,实现了Write()的接口,所以可以使用b.wr.Write(p) 将p的内容写入文件

b.flush() 会将缓存区内容写入文件,当所有写入完成后,因为缓存区会存储内容,所以需要手动flush()到文件

b.Available() 为buf可用容量,等于len(buf) - n

下图解释的是其中一种情况,即缓存区有内容,剩余p大于缓存区

 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持极客世界。

 

实例代码

读取数据:

package main ​ import ( "os" "fmt" "bufio" ) ​ func main() { /* bufio:高效io读写 buffer缓存 io:input/output ​ 将io包下的Reader,Write对象进行包装,带缓存的包装,提高读写的效率 ​ ReadBytes() ReadString() ReadLine() ​ */ ​ fileName:="/Users/ruby/Documents/pro/a/english.txt" file,err := os.Open(fileName) if err != nil{ fmt.Println(err) return } defer file.Close() ​ //创建Reader对象 //b1 := bufio.NewReader(file) //1.Read(),高效读取 //p := make([]byte,1024) //n1,err := b1.Read(p) //fmt.Println(n1) //fmt.Println(string(p[:n1])) ​ //2.ReadLine() //data,flag,err := b1.ReadLine() //fmt.Println(flag) //fmt.Println(err) //fmt.Println(data) //fmt.Println(string(data)) ​ //3.ReadString() // s1,err :=b1.ReadString('\n') // fmt.Println(err) // fmt.Println(s1) // // s1,err = b1.ReadString('\n') // fmt.Println(err) // fmt.Println(s1) // //s1,err = b1.ReadString('\n') //fmt.Println(err) //fmt.Println(s1) // //for{ // s1,err := b1.ReadString('\n') // if err == io.EOF{ // fmt.Println("读取完毕。。") // break // } // fmt.Println(s1) //} ​ //4.ReadBytes() //data,err :=b1.ReadBytes('\n') //fmt.Println(err) //fmt.Println(string(data)) ​ ​ //Scanner //s2 := "" //fmt.Scanln(&s2) //fmt.Println(s2) ​ b2 := bufio.NewReader(os.Stdin) s2, _ := b2.ReadString('\n') fmt.Println(s2) ​ }

package bytes

import "bytes"

bytes包实现了操作[]byte的常用函数。本包的函数和strings包的函数相当类似。

 

package list

import "container/list"

list包实现了双向链表。要遍历一个链表:

package ring

import "container/ring"

ring实现了环形链表的操作。

package crypto

import "crypto"

crypto包搜集了常用的密码(算法)常量。

package runtime

import "runtime"

runtime包提供和go运行时环境的互操作,如控制go程的函数。它也包括用于reflect包的低层次类型信息;参见reflect报的文档获取运行时类型系统的可编程接口。

func Gosched 

func Gosched()

Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行。

func Goexit

func Goexit()

Goexit终止调用它的go程。其它go程不会受影响。Goexit会在终止该go程前执行所有defer的函数。

在程序的main go程调用本函数,会终结该go程,而不会让main返回。因为main函数没有返回,程序会继续执行其它的go程。如果所有其它go程都退出了,程序就会崩溃。

func NumGoroutine

func NumGoroutine() int

func GC

func GC()

GC执行一次垃圾回收。

func GOROOT

func GOROOT() string

GOROOT返回Go的根目录。如果存在GOROOT环境变量,返回该变量的值;否则,返回创建Go时的根目录。

func Version

func Version() string

返回Go的版本字符串。它要么是递交的hash和创建时的日期;要么是发行标签如"go1.3"。

func NumCPU

func NumCPU() int

NumCPU返回本地机器的逻辑CPU个数。

func GOMAXPROCS

func GOMAXPROCS(n int) int

GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。本函数在调度程序优化后会去掉。

 

package sort

import "sort"

sort包提供了排序切片和用户自定义数据集的函数。

type Interface

type Interface interface { // Len方法返回集合中的元素个数 Len() int // Less方法报告索引i的元素是否比索引j的元素小 Less(i, j int) bool // Swap方法交换索引i和j的两个元素 Swap(i, j int) }

一个满足sort.Interface接口的(集合)类型可以被本包的函数进行排序。方法要求集合中的元素可以被整数索引。

 

package signal

import "os/signal"

signal包实现了对输入信号的访问。

Index

返回首页

 

func Notify(c chan<- os.Signal, sig ...os.Signal)

func Stop(c chan<- os.Signal)

Examples

返回首页

 

Notify

func Notify

func Notify(c chan<- os.Signal, sig ...os.Signal)

Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。

signal包不会为了向c发送信息而阻塞(就是说如果发送时c阻塞了,signal包会直接放弃):调用者应该保证c有足够的缓存空间可以跟上期望的信号频率。对使用单一信号用于通知的通道,缓存为1就足够了。

可以使用同一通道多次调用Notify:每一次都会扩展该通道接收的信号集。唯一从信号集去除信号的方法是调用Stop。可以使用同一信号和不同通道多次调用Notify:每一个通道都会独立接收到该信号的一个拷贝。

Example

func Stop

func Stop(c chan<- os.Signal)

Stop函数让signal包停止向c转发信号。它会取消之前使用c调用的所有Notify的效果。当Stop返回后,会保证c不再接收到任何信号。

Bugs

 本包还没在Plan 9上实现。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
util.go源码阅读发布时间:2022-07-10
下一篇:
Go语言字符串发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap