golang标准库【二】

log

golang内置了log包,实现简单的日志服务。通过调用log包的函数,可以实现简单的日志打印功能。

log包定义了Logger类型,该类型提供了一些格式化输出的方法。

log包中有3个系列的日志打印函数,分别print系列panic系列fatal系列.

函数系列说明作用
PrintPrint/Printf/Println单纯打印日志
PanicPanic/Panicf/Panicln打印日志,抛出panic异常
FatalFatal/Fatalf/Fatalln打印日志,强制结束程序(os.Exit(1)),defer函数不会执行

log包主要提供了3类接口,分别是print系列、panic系列、fatal系列,对每一类接口其提供了3种调用方式。

Print

单纯打印日志。

func TestLogPrint(t *testing.T) {
	name := "xiaojun"
	what := "dog"
	log.Print("xiaojun is a dog\n")
	log.Printf("xiaojun is a dog :%d\n", 66)
	log.Println(name, " is ", what)
}

Panic

打印出日志并且抛出panic异常,在panic之后声明的代码将不会执行。

func TestLogPanic(t *testing.T) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("panic recover , err:%v\n", err)
		}
	}()
	log.Println("xiaojun is a dog")
	log.Panic("xiaojun is a dog")
	log.Printf("运行结束")
}

Fatal

将日志内容打印输出,接着调用系统的os.Exit(1)接口,强制退出程序并返回状态1,由于直接调用系统os接口退出,defer函数不会调用。

func TestLogFatal(t *testing.T) {
	defer log.Print("defer log.....")
	log.Println("xiaojun is a dog")
	log.Fatal("xiaojun is a dog")
	log.Printf("运行结束")
}

日志配置

默认情况下log只会打印出时间,但是实际情况下我们还需要获取文件名,行号等信息,log包提供给我们定制的接口。

方法说明
func Flags() int返回标准log输出配置
func SetFlags(flag int)设置标准log输出配置
const (
    // 控制输出日志信息的细节,不能控制输出的顺序和格式。
    // 输出的日志在每一项后会有一个冒号分隔,例如2022/07/23 01:23:23.123123 /a/b/c/d.go:23: message
    Ldate         = 1 << iota     // 日期,2022/07/23
    Ltime                         // 时间,01:23:23
    Lmicroseconds                 // 微秒级别的时间,01:23:23.123123(用于增强Ltime位)
    Llongfile                     // 文件全路径名+行号,/a/b/c/d.go:23
    Lshortfile                    // 文件名+行号,d.go:23(会覆盖掉Llongfile)
    LUTC                          // 使用UTC时间
    LstdFlags     = Ldate | Ltime // 标准logger的初始值
)
func TestLogFlag(t *testing.T) {
	flag := log.Flags()
	// 2024/04/20 16:18:44 flag:3
	log.Printf("flag:%d\n", flag)
	log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)
	// 2024/04/20 16:18:44 E:/ProgramFiles/Go/project/learning-test/logtest/log_test.go:38: xiaojun is a dog
	log.Println("xiaojun is a dog")
}
前缀配置
方法说明
func Prefix() string返回日志的前缀配置
func SetPrefix(prefix string)设置日志前缀
func TestLogPrefix(t *testing.T) {
	pre := log.Prefix()
	fmt.Printf("pre:%s\n", pre)
	log.SetPrefix("[MyDog]")
	pre = log.Prefix()
	// pre:[MyDog]
	fmt.Printf("pre:%s\n", pre)
	// [MyDog]2024/04/20 16:23:40 this is xiaojun...
	log.Println("this is xiaojun...")
}
输出到文件

log包提供了func SetOutput(w io.Writer)函数,将日志输出到文件中。

func TestLogOutput(t *testing.T) {
	f, _ := os.OpenFile("a.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
	log.SetOutput(f)
	log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)
	log.Println("xioajun is a dog.")
}
自定义Logger

log包中提供了func New(out io.Writer, prefix string, flag int) *Logger函数来实现自定义logger。

func TestNewLogger(t *testing.T) {
	var logger *log.Logger
	f, err := os.OpenFile("b.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	logger = log.New(f, "[MyDog]", log.Ldate|log.Ltime|log.Lshortfile)

	logger.Println("this is Betry... My little Dog.")
}

errors

errors包实现了操作错误的函数。go语言使用error类型来返回函数执行过程中遇到的错误,如果返回的error值为nil,则表示未遇到错误,否则error会返回一个字符串,用于说明遇到了什么错误。

使用

func main() {
	name, err := checkName("tom")
	if err != nil {
		fmt.Printf("%v\n", err)
	} else {
		fmt.Printf("%s\n", name)
	}
}

func checkName(s string) (string, error) {
	if s == "Betry" {
		return s, nil
	} else {
		return "", errors.New("its not an animal name")
	}
}

自定义错误

可以通过实现error接口的方式,来扩展错误返回。

type MyError struct {
	Code int
	Msg  string
	t    time.Time
}

func (e *MyError) Error() string {
	return fmt.Sprintf("%s code:%d %s", e.t.Format("2006-01-02 15:04:05"), e.Code, e.Msg)
}

func Fail(c int, m string) error {
	return &MyError{Code: c, Msg: m, t: time.Now()}
}

func Success() error {
	return &MyError{200, "success", time.Now()}
}

func (e *MyError) Fail(c int, m string) error {
	e.Code = c
	e.Msg = m
	e.t = time.Now()
	return e
}

func (e *MyError) Success() error {
	e.Code = 200
	e.Msg = "success"
	e.t = time.Now()
	return e
}

func main() {
	if err := new(MyError).Fail(404, "this my error"); err != nil {
		panic(err)
	}
}

bytes

bytes包提供了对字节切片进行读写操作的一系列函数,字节切片处理的函数分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等。

常用函数

转换
函数说明
func ToUpper(s []byte) []byte将 s 中的所有字符修改为大写格式返回。
func ToLower(s []byte) []byte将 s 中的所有字符修改为小写格式返回
func ToTitle(s []byte) []byte将 s 中的所有字符修改为标题格式返回
func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte使用指定的映射表将 s 中的所有字符修改为大写格式返回
func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte使用指定的映射表将 s 中的所有字符修改为小写格式返回
func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte使用指定的映射表将 s 中的所有字符修改为标题格式返回
func Title(s []byte) []byte将 s 中的所有单词的首字符修改为 Title 格式返回。(缺点:不能很好的处理以 Unicode 标点符号分隔的单词。)
func TestToUpper(t *testing.T) {
	var a = []byte("xiaojun betry dog")
	var b []byte = bytes.ToUpper(a)
	fmt.Println(*(*string)(unsafe.Pointer(&b)))
}

func TestToLower(t *testing.T) {
	var a = []byte("XIAOJUN BETRY DOG")
	var b []byte = bytes.ToLower(a)
	fmt.Println(string(b))
}

func TestToTitle(t *testing.T) {
	a := []byte("xiaojun betry 是我家狗")
	b := bytes.ToTitle(a)
	fmt.Println(string(b))
}

func TestToUpperSpecial(t *testing.T) {
	a := []byte("what my little dog")
	b := bytes.ToUpperSpecial(unicode.CaseRanges, a)
	fmt.Println(string(b))
}

func TestToLowerSpecial(t *testing.T) {
	a := []byte("XIAOJUN BETRY DOG")
	b := bytes.ToLowerSpecial(unicode.CaseRanges, a)
	fmt.Println(string(b))
}

func TestToTitleSpecial(t *testing.T) {
	a := []byte("xiaojun betry dog")
	b := bytes.ToTitleSpecial(unicode.CaseRanges, a)
	fmt.Println(string(b))
}

func TestTitle(t *testing.T) {
	a := []byte("xiaojun betry 是我家狗")
	b := bytes.Title(a)
	fmt.Println(string(b))
}
比较
函数说明
func Compare(a, b []byte) int比较两个 []byte,nil 参数相当于空 []byte。a < b 返回 -1;a == b 返回 0;a > b 返回 1
func Equal(a, b []byte) bool判断 a、b 是否相等,nil 参数相当于空 []byte
func EqualFold(s, t []byte) bool判断 s、t 是否相似,忽略大写、小写、标题三种格式的区别
func TestCompare(t *testing.T) {
	a := []byte("xiaojun betry dog")
	b := []byte("XIAOJUN BETRY DOG")
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(bytes.Compare(a, b))
}

func TestEqual(t *testing.T) {
	a := []byte("xiaojun betry dog")
	b := []byte("XIAOJUN BETRY DOG")
	fmt.Println(bytes.Equal(a, b))
}

func TestEqualFold(t *testing.T) {
	a := []byte("xiaojun betry dog")
	b := []byte("XIAOJUN BETRY DOG")
	fmt.Println(bytes.EqualFold(a, b))
}
清理
函数说明
func Trim(s []byte, cutset string) []byte去掉 s 两边包含在 cutset 中的字符(返回 s 的切片)
func TrimLeft(s []byte, cutset string) []byte去掉 s 左边包含在 cutset 中的字符(返回 s 的切片)
func TrimRight(s []byte, cutset string) []byte去掉 s 右边包含在 cutset 中的字符(返回 s 的切片)
func TrimFunc(s []byte, f func(r rune) bool) []byte去掉 s 两边符合 f函数====返回值是true还是false 要求的字符(返回 s 的切片)
func TrimLeftFunc(s []byte, f func(r rune) bool) []byte去掉 s左边符合 f函数====返回值是true还是false 要求的字符(返回 s 的切片)
func TrimRightFunc(s []byte, f func(r rune) bool) []byte去掉 s右边符合 f函数====返回值是true还是false 要求的字符(返回 s 的切片)
func TrimSpace(s []byte) []byte去掉 s 两边的空白(unicode.IsSpace)(返回 s 的切片)
func TrimPrefix(s, prefix []byte) []byte去掉 s 的前缀 prefix(返回 s 的切片)
func TrimSuffix(s, suffix []byte) []byte去掉 s 的后缀 suffix(返回 s 的切片)
func TestTrim(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	for _, b := range a {
		fmt.Println(string(bytes.Trim(b, "!!.")))
	}
}

func TestTrimLeft(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	for _, b := range a {
		fmt.Println(string(bytes.TrimLeft(b, "!!.")))
	}
}

func TestTrimRight(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	for _, b := range a {
		fmt.Println(string(bytes.TrimRight(b, "!!.")))
	}
}

func TestTrimFunc(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	f := func(r rune) bool {
		return bytes.ContainsRune([]byte("!!."), r)
	}

	for _, b := range a {
		fmt.Println(string(bytes.TrimFunc(b, f)))
	}
}

func TestTrimLeftFunc(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	f := func(r rune) bool {
		return bytes.ContainsRune([]byte("!!."), r)
	}

	for _, b := range a {
		fmt.Println(string(bytes.TrimLeftFunc(b, f)))
	}
}

func TestTrimRightFunc(t *testing.T) {
	a := [][]byte{
		[]byte("!xiaojun betry dog!"),
		[]byte("!xiaojun betry dog!"),
		[]byte(".xiaojun betry dog."),
	}

	f := func(r rune) bool {
		return bytes.ContainsRune([]byte("!!."), r)
	}

	for _, b := range a {
		fmt.Println(string(bytes.TrimRightFunc(b, f)))
	}
}

func TestTrimSpace(t *testing.T) {
	a := []byte(" xiaojun betry dog ")
	fmt.Println(string(bytes.TrimSpace(a)))
}

func TestTrimPrefix(t *testing.T) {
	a := []byte("xiaojun betry dog")
	fmt.Println(string(bytes.TrimPrefix(a, []byte("xiaojun"))))
}

func TestTrimSuffix(t *testing.T) {
	a := []byte("xiaojun betry dog@")
	fmt.Println(string(bytes.TrimSuffix(a, []byte("@"))))
}
拆合
函数说明
func Split(s, sep []byte) [][]byteSplit 以 sep 为分隔符将 s 切分成多个子串,结果不包含分隔符。如果 sep 为空,则将 s 切分成 Unicode 字符列表。
func SplitN(s, sep []byte, n int) [][]byteSplitN 可以指定切分次数 n,超出 n 的部分将不进行切分。
func SplitAfter(s, sep []byte) [][]byte功能同 Split,只不过结果包含分隔符(在各个子串尾部)。
func SplitAfterN(s, sep []byte, n int) [][]byte功能同 SplitN,只不过结果包含分隔符(在各个子串尾部)。
func Fields(s []byte) [][]byte以连续空白为分隔符将 s 切分成多个子串,结果不包含分隔符。
func FieldsFunc(s []byte, f func(rune) bool) [][]byte以符合 f 的字符为分隔符将 s 切分成多个子串,结果不包含分隔符。
func Join(s [][]byte, sep []byte) []byte以 sep 为连接符,将子串列表 s 连接成一个字节串。
func Repeat(b []byte, count int) []byte将子串 b 重复 count 次后返回。
func TestSplit(t *testing.T) {
	a := []byte("  xiaojun betry dog ")
	split := bytes.Split(a, []byte(" "))
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestSplitN(t *testing.T) {
	a := []byte("  xiaojun betry dog ")
	split := bytes.SplitN(a, []byte(" "), 4)
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestSplitAfter(t *testing.T) {
	a := []byte("  xiaojun betry dog ")
	split := bytes.SplitAfter(a, []byte(" "))
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestSplitAfterN(t *testing.T) {
	a := []byte("  xiaojun betry dog ")
	split := bytes.SplitAfterN(a, []byte(" "), 4)
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestFields(t *testing.T) {
	a := []byte("  xiaojun betry dog ")
	split := bytes.Fields(a)
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestFieldsFunc(t *testing.T) {
	a := []byte("  xiaojun betry doog ")
	f := func(r rune) bool {
		return bytes.ContainsRune([]byte("o"), r)
	}
	split := bytes.FieldsFunc(a, f)
	for _, s := range split {
		fmt.Println(string(s))
	}
}

func TestJoint(t *testing.T) {
	a := [][]byte{
		[]byte("betry is dog "),
		[]byte("betry is dog "),
		[]byte("xiaojun betry is dog "),
	}
	fmt.Println(string(bytes.Join(a, []byte("---"))))
}

func TestRepeat(t *testing.T) {
	a := []byte("betry is dog ")
	fmt.Println(string(bytes.Repeat(a, 3)))
}
字串
函数说明
func HasPrefix(s, prefix []byte) bool判断 s 是否有前缀 prefix
func HasSuffix(s, suffix []byte) bool判断 s 是否有后缀 suffix
func Contains(b, subslice []byte) bool判断 b 中是否包含子串 subslice
func ContainsRune(b []byte, r rune) bool判断 b 中是否包含子串 字符 r
func ContainsAny(b []byte, chars string) bool判断 b 中是否包含 chars 中的任何一个字符
func Index(s, sep []byte) int查找子串 sep在 s 中第一次出现的位置,找不到则返回 -1
func IndexByte(s []byte, c byte) int查找子串 字节 c在 s 中第一次出现的位置,找不到则返回 -1
func IndexRune(s []byte, r rune) int查找子串字符 r在 s 中第一次出现的位置,找不到则返回 -1
func IndexAny(s []byte, chars string) int查找 chars 中的任何一个字符在 s 中第一次出现的位置,找不到则返回 -1。
func IndexFunc(s []byte, f func(r rune) bool) int查找符合 f 的字符在 s 中第一次出现的位置,找不到则返回 -1。
func LastIndex(s, sep []byte) int功能同上,只不过查找最后一次出现的位置。
func LastIndexByte(s []byte, c byte) int功能同上,只不过查找最后一次出现的位置。
func LastIndexAny(s []byte, chars string) int功能同上,只不过查找最后一次出现的位置。
func LastIndexFunc(s []byte, f func(r rune) bool) int功能同上,只不过查找最后一次出现的位置。
func Count(s, sep []byte) int获取 sep 在 s 中出现的次数(sep 不能重叠)。
func TestHasPrefix(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.HasPrefix(a, []byte("b")))
}

func TestHasSuffix(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.HasSuffix(a, []byte("dog")))
}

func TestContains(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.Contains(a, []byte("dog")))
}

func TestContainsRune(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.ContainsRune(a, 100))
}

func TestContainsAny(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.ContainsAny(a, "dogxiaojun"))
}
func TestIndex(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.Index(a, []byte("xiao")))
}

func TestIndexByte(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.IndexByte(a, 'x'))
}
func TestIndexRune(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.IndexRune(a, 'i'))
}

func TestIndexAny(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.IndexAny(a, "dog betry"))
}

func TestIndexFunc(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.IndexFunc(a, unicode.IsSpace))
}

func TestLastIndex(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.LastIndex(a, []byte("o")))
}
func TestLastIndexByte(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.LastIndexByte(a, 'o'))
}
func TestLastIndexAny(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.LastIndexAny(a, "dogxiaojun"))
}
func TestLastIndexFunc(t *testing.T) {
	a := []byte("betry xiaojun is a dog")
	fmt.Println(bytes.LastIndexFunc(a, unicode.IsNumber))
}
func TestCount(t *testing.T) {
	a := []byte("betry xiaojun is a dog dog")
	fmt.Println(bytes.Count(a, []byte("dog")))
}
替换
函数说明
func Replace(s, old, new []byte, n int) []byte将 s 中前 n 个 old 替换为 new,n < 0 则替换全部。
func Map(mapping func(r rune) rune, s []byte) []byte将 s 中的字符替换为 mapping的返回值,如果 mapping 返回负值,则丢弃该字符。
func Runes(s []byte) []rune将 s 转换为 []rune 类型返回
func TestReplace(t *testing.T) {
	a := []byte("betry betry betry xiaojun is a dog dog")
	old := []byte("betry")
	news := []byte("xiaojun")

	fmt.Printf("%s\n", bytes.Replace(a, old, news, 0))
	fmt.Printf("%s\n", bytes.Replace(a, old, news, 1))
	fmt.Printf("%s\n", bytes.Replace(a, old, news, 2))
	fmt.Printf("%s\n", bytes.Replace(a, old, news, -1))
}

func TestMap(t *testing.T) {
	a := []byte("betry xiaojun is a dog dog")
	f := func(r rune) rune {
		if r == 'b' {
			return 'A'
		} else {
			return r
		}
	}

	fmt.Println(string(bytes.Map(f, a)))
}

func TestRunes(t *testing.T) {
	a := []byte("betry xiaojun is a dog dog")
	fmt.Println(string(bytes.Runes(a)))
}

Buffer类型

缓冲区是具有读取和写入方法的可变大小的字节缓冲区,Buffer的零值是准备使用的空缓冲区。

声明
func TestBuffer(t *testing.T) {
    // 直接定义一个Buffer变量,不用初始化,可以直接使用
	var b bytes.Buffer
    // 使用New返回Buffer变量
	b1 := new(bytes.Buffer)
    // 从一个[]byte切片,构造一个Buffer
	b2 := bytes.NewBuffer([]byte{})
    // 从一个string变量,构造一个Buffer
	b3 := bytes.NewBufferString("")
	// bytes.Buffer *bytes.Buffer *bytes.Buffer *bytes.Buffer
	fmt.Printf("%T %T %T %T", b, b1, b2, b3)
}
写入数据
func TestBufferWrite(t *testing.T) {
	var b bytes.Buffer
	// 将切片d写入Buffer数据
	b.Write([]byte("betry xiaojun "))
	// 将字符串s写入Buffer尾部
	b.WriteString("is a dog")
	// 将一个rune类型的数据放到缓冲器的尾部
	b.WriteRune('!')
	f, _ := os.OpenFile("a.txt", os.O_CREATE|os.O_RDWR, 0666)
	defer func() { _ = f.Close() }()
	// 将Buffer中的内容输出到实现了io.Writer接口的可写入对象中
	_, _ = b.WriteTo(f)
}
读取数据
func TestBufferRead(t *testing.T) {
	b := bytes.NewBufferString("betry xiaojun is a dog dog")
	r1 := make([]byte, 3)
    // 一次读取n个byte到c容器中,每次读取新的n个byte覆盖c中原来的内容
	r1n, _ := b.Read(r1)
	fmt.Println(string(r1[:r1n]))
    // 读取第一个byte,b的第一个byte被拿掉,赋值给 a => a, _ := b.ReadByte()
	r2, _ := b.ReadByte()
	fmt.Println(string(r2))
    // 读取第一个rune,b的第一个rune被拿掉,赋值给 r => r, _ := b.ReadRune()
	r3, _, _ := b.ReadRune()
	fmt.Println(string(r3))
    // 需要一个byte作为分隔符,读的时候从缓冲器里找第一个出现的分隔符(delim),找到后,把从缓冲器头部开始到分隔符之间的所有byte进行返回,作为byte类型的slice,返回后,缓冲器也会空掉一部分
	r4, _ := b.ReadBytes('o')
	fmt.Println(string(r4))
    // 需要一个byte作为分隔符,读的时候从缓冲器里找第一个出现的分隔符(delim),找到后,把从缓冲器头部开始到分隔符之间的所有byte进行返回,作为字符串返回,返回后,缓冲器也会空掉一部分
	r5, _ := b.ReadString('n')
	fmt.Println(r5)
    // 从一个实现io.Reader接口的r,把r里的内容读到缓冲器里,n返回读的数量
	reader := bytes.NewReader([]byte("-dog xiaoxun"))
	_, _ = b.ReadFrom(reader)
	fmt.Println(b.String())
}
其他方法
func TestBufferOther(t *testing.T) {
	b := bytes.NewBufferString("betry xiaojun is a dog dog")
	// 未读取部分的数据长度
	fmt.Println(b.Len())
	// 获取缓存的容量
	fmt.Println(b.Cap())
	// 读取前 n 字节的数据并以切片形式返回,如果数据长度小于 n,则全部读取。切片只在下一次读写操作前合法。
	fmt.Println(string(b.Next(4)))
	// 引用未读取部分的数据切片(不移动读取位置)
	fmt.Println(string(b.Bytes()))
	// 返回未读取部分的数据字符串(不移动读取位置)
	fmt.Println(b.String())
	// 自动增加缓存容量,以保证有 n 字节的剩余空间。如果 n 小于 0 或无法增加容量则会 panic。
	b.Grow(10)
	fmt.Println(b.Cap())
	// 将数据长度截短到 n 字节,如果 n 小于 0 或大于 Cap 则 panic。
	b.Truncate(10)
	fmt.Println(b.String())
	//  重设缓冲区,清空所有数据(包括初始内容)。
	b.Reset()
	fmt.Println(b.String())
}

Reader类型

Reader实现了io.Reader, io.ReaderAt, io.WriterTo, io.Seeker, io.ByteScanner, io.RuneScanner接口

func TestBufferReader(t *testing.T) {
	data := "betry xiaojun is a dog dog"
	// 将 data 包装成 bytes.Reader 对象。
	reader := bytes.NewReader([]byte(data))
	// 返回未读取部分的数据长度
	fmt.Println(reader.Len())
	// 返回底层数据的总长度,方便 ReadAt 使用,返回值永远不变。
	fmt.Println(reader.Size())
	// 将底层数据切换为 b,同时复位所有标记(读取位置等信息)
	reader.Reset([]byte("xiaojun dog"))
	b, _ := ioutil.ReadAll(reader)
	fmt.Printf("%s \n", b)
}

func TestBufferReaderAt(t *testing.T) {
	reader := bytes.NewReader([]byte("betry xiaojun is a dog dog"))

	b := make([]byte, 5)
	for {
		n, err := reader.Read(b)
		if n == 0 || err == io.EOF {
			break
		}
		fmt.Print(string(b[:n]))
	}
	fmt.Println()

	reader.Seek(0, 0)
	offset := int64(0)
	for {
		n, err := reader.ReadAt(b, offset)
		if n > 0 {
			offset += int64(n)
			fmt.Print(string(b[:n]))
		}
		if err == io.EOF || n == 0 {
			break
		}
	}
}

io

io包中提供I/O原始操作的一系列接口。它主要包装了一些已有的实现,如 os 包中的那些,并将这些抽象成为实用性的功能和一些其他相关的接口。

错误变量

函数说明
var EOF = errors.New(“EOF”)正常输入结束Read返回EOF,如果在一个结构化数据流中EOF在不期望的位置出现了,则应返回错误ErrUnexpectedEOF或者其它给出更多细节的错误。
var ErrClosedPipe = errors.New(“io: read/write on closed pipe”)当从一个已关闭的Pipe读取或者写入时,会返回ErrClosedPipe。
var ErrNoProgress = errors.New(“multiple Read calls return no data or error”)某些使用io.Reader接口的客户端如果多次调用Read都不返回数据也不返回错误时,就会返回本错误,一般来说是io.Reader的实现有问题的标志。
var ErrShortBuffer = errors.New(“short buffer”)ErrShortBuffer表示读取操作需要大缓冲,但提供的缓冲不够大。
var ErrShortWrite = errors.New(“short write”)ErrShortWrite表示写入操作写入的数据比提供的少,却没有显式的返回错误。
var ErrUnexpectedEOF = errors.New(“unexpected EOF”)ErrUnexpectedEOF表示在读取一个固定尺寸的块或者数据结构时,在读取未完全时遇到了EOF。

基础接口

Reader

Read 将 len个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len)以及任何遇到的错误。即使 Read 返回的 n < len,它也会在调用过程中使用 p的全部作为暂存空间。若一些数据可用但不到 len 个字节,Read 会照例返回可用的东西,而不是等待更多。

func TestReader(t *testing.T) {
	f, _ := os.OpenFile("a.txt", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
	defer func() { _ = f.Close() }()
	buf := bytes.NewBufferString("xiaojun is a dog")
	_, _ = buf.WriteTo(f)

	_, _ = f.Seek(0, 0)
	b := make([]byte, 4)
	var ret []byte
	for {
		n, err := f.Read(b)
		if n == 0 || err == io.EOF {
			fmt.Println("读取结束")
			break
		}
		ret = append(ret, b[:n]...)
	}

	fmt.Println(*(*string)(unsafe.Pointer(&ret)))
}
Writer

Write 将 len个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数n(0 <= n <= len)以及任何遇到的引起写入提前停止的错误。若 Write 返回的n < len,它就必须返回一个非nil的错误。Write 不能修改此切片的数据,即便它是临时的。

func TestWriter(t *testing.T) {
	f, _ := os.OpenFile("b.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	defer func() { _ = f.Close() }()
	_, _ = f.Write([]byte("xiaojun is a dog"))
}
Seeker

Seeker 用来移动数据的读写指针

Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的

  • whence 的含义:

    • 如果 whence 为 0:表示从数据的开头开始移动指针
    • 如果 whence 为 1:表示从数据的当前指针位置开始移动指针
    • 如果 whence 为 2:表示从数据的尾部开始移动指针
  • offset 是指针移动的偏移量

    返回移动后的指针位置和移动过程中遇到的任何错误

func TestSeek(t *testing.T) {
	f, _ := os.OpenFile("b.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	defer func() { _ = f.Close() }()
	_, _ = f.Write([]byte("xiaojun is a dog"))
	_, _ = f.Seek(4, 0)
	s, _ := ioutil.ReadAll(f)
	fmt.Println(string(s))
}
Closer

Closer关闭的接口, 带有Close() 方法, 但是行为没有定义,所以 可以特定行为来实现
在整个标准库内都没有对Closer的引用,只有实现,用法都是开启某某连接/流,在用完/报错后在进行Close的操作。

func TestClose(t *testing.T) {
	http.HandleFunc("", func(w http.ResponseWriter, r *http.Request) {
		b, _ := ioutil.ReadAll(r.Body)
		buf := bytes.NewBuffer(b)
		r.Body = io.NopCloser(buf)
	})
}

组合接口

ReadWriter

ReadWriter接口聚合了基本的读写操作。

ReadCloser

ReadCloser就是Reader+Closer,例如在ioutil中的NopCloser方法返回的就是一个ReadCloser,但是里面的Close就是个空函数,毫无作用。

WriteCloser

WriteCloser接口聚合了基本的写入和关闭操作。

ReadWriteCloser

ReadWriteCloser接口聚合了基本的读写和关闭操作。

ReadSeeker

ReadSeeker接口聚合了基本的读取和移位操作。

WriteSeeker

WriteSeeker接口聚合了基本的写入和移位操作。

ReadWriteSeeker

ReadWriteSeeker接口聚合了基本的读写和移位操作。

指定读写器读写接口

ReaderFrom

ReadFrom 从 r 中读取数据到对象的数据流中。

WriterTo

WriterTo 将对象的数据流写入到 w 中。

指定偏移量读写接口

ReaderAt

ReadAt 从对象数据流的 off 处读出数据到 p 中

  • 忽略数据的读写指针,从数据的起始位置偏移 off 处开始读取
  • 如果对象的数据流只有部分可用,不足以填满 p则 ReadAt 将等待所有数据可用之后,继续向 p 中写入直到将 p 填满后再返回,在这点上 ReadAt 要比 Read 更严格
  • 返回读取的字节数 n 和读取时遇到的错误
  • 如果 n < len,则需要返回一个 err 值来说明为什么没有将 p 填满(比如 EOF)
  • 如果 n > len,而且对象的数据没有全部读完,则err 将返回 nil
  • 如果 n = len,而且对象的数据刚好全部读完,则err 将返回 EOF 或者 nil(不确定)
WriterAt

WriteAt 将 p 中的数据写入到对象数据流的 off 处

  • 忽略数据的读写指针,从数据的起始位置偏移 off 处开始写入
  • 返回写入的字节数和写入时遇到的错误
  • 如果 n < len,则必须返回一个 err 值来说明为什么没有将 p 完全写入

单个字节读写接口

ByteReader接口

ByteReader是基本的ReadByte方法的包装。ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

ByteScanner接口

ByteScanner接口在基本的ReadByte方法之外还添加了UnreadByte方法。UnreadByte方法让下一次调用ReadByte时返回之前调用ReadByte时返回的同一个字节。连续调用两次UnreadByte方法而中间没有调用ReadByte时,可能会导致错误。

ByteWriter接口

包装 WriteByte 单个字节写入方法的接口。

RuneReader接口

ReadRune 方法的包装,读取单个UTF-8编码的Unicode字符,并返回rune及其字节大小。如果没有可用字符,将设置err。

RuneScanner接口

RuneScanner接口在基本的ReadRune方法之外还添加了UnreadRune方法。UnreadRune方法让下一次调用ReadRune时返回之前调用ReadRune时返回的同一个utf-8字符。连续调用两次UnreadRune方法而中间没有调用ReadRune时,可能会导致错误。

StringWriter接口

字符串写入方法WriteString的包装。

结构体

LimitedReader

LimitedReader从R读取,但将返回的数据量限制为N个字节。每次读取更新N以标记剩余可以读取的字节数。Read在N<=0时或基础R返回EOF时返回EOF。

PipeReader

PipeReader是一个管道的读取端。

具体实现方法有:

  1. func (r *PipeReader) Read(data []byte) (n int, err error)
    Read实现了标准的读取接口:它从管道中读取数据,阻塞直到写入端到达或写入端被关闭。如果用错误关闭写入端,则返回错误为ERR;否则ERR为EOF。
  2. func (r *PipeReader) Close() error
    Close关闭读取器;关闭后如果对管道的写入端进行写入操作,就会返回(0, ErrClosedPip)。
  3. func (r *PipeReader) CloseWithError(err error) error
    CloseWithError类似Close方法,但将调用Write时返回的错误改为err。
SectionReader

SectionReader在ReaderAt的基础上实现了Read,Seek和ReadAt。
具体实现方法有:

  1. func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader
    结构体SectionReader的创建方法
    NewSectionReader返回一个SectionReader,它从r开始读取,偏移量为off,并在n个字节后以EOF停止。
  2. func (s *SectionReader) Read(p []byte) (n int, err error)
    实现了接口Reader的Read方法
  3. func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error)
    实现了接口ReaderAt的ReadAt方法
  4. func (s *SectionReader) Seek(offset int64, whence int) (int64, error)
    实现了接口Seeker的Seek方法
    func (*SectionReader) Size
  5. func (s *SectionReader) Size() int64
    Size返回以字节为单位的片段大小。

供外部调用的函数

Copy

将副本从src复制到dst,直到在src上达到EOF或发生错误。它返回复制的字节数和复制时遇到的第一个错误(如果有)。 成功的复制将返回err == nil而不是err == EOF。因为复制被定义为从src读取直到EOF,所以它不会将读取的EOF视为要报告的错误。 如果src实现WriterTo接口,则通过调用src.WriteTo(dst)实现该副本。否则,如果dst实现了ReaderFrom接口,则通过调用dst.ReadFrom(src)实现该副本。

func TestCopy(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")
	if i, err := io.Copy(os.Stdout, r); err != nil {
		log.Fatal(err)
	} else {
		fmt.Println(i)
	}
}
CopyBuffer

CopyBuffer与Copy相同,区别在于CopyBuffer逐步遍历提供的缓冲区(如果需要),而不是分配临时缓冲区。如果buf为nil,则分配一个;如果长度为零,则CopyBuffer会panic报错。 如果src实现WriterTo或dst实现ReaderFrom,则buf将不用于执行复制。

func TestCopyBuffer(t *testing.T) {
	s1 := strings.NewReader("xiaojun is a dog\n")
	s2 := bytes.NewReader([]byte("betry is a dog\n"))
	b := make([]byte, 4)

	if _, err := io.CopyBuffer(os.Stdout, s1, b); err != nil {
		log.Fatal(err)
	}

	if _, err := io.CopyBuffer(os.Stdout, s2, b); err != nil {
		log.Fatal(err)
	}
}
CopyN

CopyN将n个字节(或直到出错)从src复制到dst。它返回复制的字节数以及复制时遇到的最早错误。返回时,只有err == nil时,writte == n。 如果dst实现了ReaderFrom接口,则使用该接口实现副本。

func TestCopyN(t *testing.T) {
	s1 := strings.NewReader("xiaojun is a dog\n")

	if _, err := io.CopyN(os.Stdout, s1, 4); err != nil {
		log.Fatal(err)
	}
}
LimitReader

LimitedReader从r读取,但将返回的数据量限制为n个字节。每次读取更新n以标记剩余可以读取的字节数。Read在n<=0时或基础r返回EOF时返回EOF。

func TestLimitReader(t *testing.T) {
	r1 := strings.NewReader("肖俊 is a dog\n")
	r2 := io.LimitReader(r1, 4)
	if _, err := io.Copy(os.Stdout, r2); err != nil {
		log.Fatal(err)
	}
}
MultiReader

MultiReader返回一个Reader,它是所提供的输入阅读器的逻辑串联。它们被顺序读取。一旦所有输入均返回EOF,读取将返回EOF。如果任何读取器返回非零,非EOF错误,则Read将返回该错误。

func TestMultiReader(t *testing.T) {
	r1 := strings.NewReader("betry")
	r2 := bytes.NewReader([]byte(" xiaojun"))
	r3 := strings.NewReader(" is a dog\n")
	r := io.MultiReader(r1, r2, r3)
	if _, err := io.Copy(os.Stdout, r); err != nil {
		log.Fatal(err)
	}
}
MultiWriter

MultiWriter创建一个Writers,将其写入复制到所有提供的写入器中,类似于Unix tee(1)命令。 每个写入一次写入每个列出的写入器。如果列出的写程序返回错误,则整个写操作将停止并返回错误;它不会在列表中继续下去。

func TestMultiWriter(t *testing.T) {
	r1 := strings.NewReader("xiaojun is a dog\n")
	var b1, b2 bytes.Buffer
	w := io.MultiWriter(&b1, &b2)
	if _, err := io.Copy(w, r1); err != nil {
		log.Fatal(err)
	}
	fmt.Print(b1.String())
	fmt.Print(b2.String())
}
Pipe

Pipe创建一个同步的内存管道。
可用于连接期望io.Reader的代码和期望io.Writer的代码。

管道上的读和写是一对一匹配的,除非需要多次读取才能使用单次写入。也就是说,每次对PipeWriter的写入都将阻塞,直到它满足从PipeReader读取的一个或多个读取,这些读取会完全消耗已写入的数据。

数据直接从Write复制到相应的Read (或Reads);没有内部缓冲。
对读的并行调用和对写的并行调用也是安全的:单个调用将按顺序执行。

func TestPipe(t *testing.T) {
	r, w := io.Pipe()
	go func() {
		fmt.Fprintln(w, "xiaojun is a dog")
		w.Write([]byte("betry is a dog\n"))
		defer func() { _ = w.Close() }()
	}()

	if _, err := io.Copy(os.Stdout, r); err != nil {
		log.Fatal(err)
	}
	defer func() { _ = r.Close() }()
}
ReadAll

ReadAll从r读取,直到出现错误或EOF,并返回其读取的数据。成功的调用返回errnil,而不是errEOF。由于ReadAll定义为从src读取直到EOF,因此它不会将读取的EOF视为要报告的错误。

func TestReadAll(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog")
	b, _ := io.ReadAll(r)
	fmt.Println(string(b))
}
ReadAtLeast

ReadAtLeast从r读取到buf,直到它至少读取了min字节。它返回复制的字节数n,如果读取的字节数少则返回错误。仅当未读取任何字节时,错误才是EOF。如果在读取少于最小字节后发生EOF,则ReadAtLeast返回ErrUnexpectedEOF。如果min大于buf的长度,则ReadAtLeast返回ErrShortBuffer。返回时,当且仅当err == nil时,n> = min。

func TestReadAtLeast(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")

	buf := make([]byte, 14)
	if _, err := io.ReadAtLeast(r, buf, 4); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", buf)

	// error: short buffer
	shortBuf := make([]byte, 3)
	if _, err := io.ReadAtLeast(r, shortBuf, 4); err != nil {
		fmt.Println("error:", err)
	}

	// error: unexpected EOF
	longBuf := make([]byte, 64)
	if _, err := io.ReadAtLeast(r, longBuf, 64); err != nil {
		fmt.Println("error:", err)
	}
}
ReadFull

ReadFull将r中的len(buf)个字节准确地读取到buf中。它返回复制的字节数,如果读取的字节数少则返回错误。仅当未读取任何字节时,错误才是EOF。如果在读取了一些但不是全部字节后发生EOF,则ReadFull返回ErrUnexpectedEOF。返回时,当且仅当err == nil时,n == len(buf)。

func TestReadFull(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")

	buf := make([]byte, 4)
	if _, err := io.ReadFull(r, buf); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", buf)

	// error: unexpected EOF
	longBuf := make([]byte, 64)
	if _, err := io.ReadFull(r, longBuf); err != nil {
		fmt.Println("error:", err)
	}
}
SectionReader

sectionReader在ReaderAt的基础上实现了Read,Seek和ReadAt。

NewSectionReader

NewSectionReader返回一个SectionReader,它从r开始读取偏移量off,并在n个字节后以EOF停止。

func TestSectionReader(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")
	reader := io.NewSectionReader(r, 4, 10)
	_, _ = io.Copy(os.Stdout, reader)
}
SectionReader.Read

实现了接口Reader的Read方法。

func TestSectionReade(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")
	reader := io.NewSectionReader(r, 4, 10)

	b := make([]byte, 2)
	for {
		n, err := reader.Read(b)
		if n == 0 || err == io.EOF {
			break
		}
		fmt.Printf("%s", b[:n])
	}
}

SectionReader.ReadAt

实现了接口ReaderAt的ReadAt方法。

func TestSectionReadeAt(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog\n")
	reader := io.NewSectionReader(r, 4, 10)

	b := make([]byte, 2)
	offset := int64(0)
	for {
		n, err := reader.ReadAt(b, offset)
		if n > 0 {
			offset = offset + int64(n)
			fmt.Printf("%s", b[:n])
		}
		if err == io.EOF {
			break
		}
	}
}
SectionReader.Seek

实现了接口Seeker的Seek方法。

func TestSectionReadeAt(t *testing.T) {
	r := strings.NewReader("betry xiaojun is a dog\n")
	r1 := io.NewSectionReader(r, 6, 15)
	_, _ = r1.Seek(4, io.SeekStart)
	_, _ = io.Copy(os.Stdout, r1)
}
SectionReader.Size

Size返回以字节为单位的片段大小。

func TestSectionSize(t *testing.T) {
	r := strings.NewReader("betry xiaojun is a dog\n")
	r1 := io.NewSectionReader(r, 6, 15)
	// 15
	fmt.Println(r1.Size())
}
WriteString

WriteString将字符串s的内容写入w,w接受字节片。如果w实现StringWriter,则直接调用其WriteString方法。否则,w.Write只调用一次。

func TestWriteString(t *testing.T) {
	io.WriteString(os.Stdout, "xiaojun is a dog")
}
TeeReader

TeeReader返回一个Reader,该Reader向w写入从r读取的内容。通过r执行的所有r读取均与对w的相应写入匹配。没有内部缓冲-写入必须在读取完成之前完成。写入时遇到的任何错误均报告为读取错误。

func TestTeeReader(t *testing.T) {
	r := strings.NewReader("xiaojun is a dog")
	reader := io.TeeReader(r, os.Stdout)
	reader.Read(make([]byte, 4))
}
  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值