Go语言中缓冲bufio的原理解读与应用实战

 更新时间:2024年10月22日 09:38:50   作者:景天科技苑  
Go语言标准库中的bufio包提供了带缓冲的I/O操作,它通过封装io.Reader和io.Writer接口,减少频繁的I/O操作,提高读写效率,本文就来详细的介绍一下,感兴趣的可以学习

bufio是Go语言标准库中的一个重要包,它提供了带缓冲的I/O操作,用于包装io.Reader或io.Writer对象,以减少I/O操作的次数,从而提高读写性能。本文将结合实际案例,详细讲解bufio在Go语言中的用法。

Go语言自带的IO操作包。bufio,使用这个包可以大幅提升文件的读写效率。

  • buf: 缓冲区.
  • io操作效率本身是还可以的,频繁访问本地磁盘文件(效率低)

所以说 bufio ,提供了一个缓冲区,读和写都先在缓冲区中,最后再一次性读取或者写入到文件里,降低访问本地磁盘的次数。

在这里插入图片描述

一、bufio的基本概念和原理

bufio的主要作用是减少I/O操作的次数,提供读写性能。其工作原理是通过在内存中维护一个缓冲区,先将数据读入缓冲区,再从缓冲区中读取数据,从而减少对底层I/O设备的访问次数。

bufio的主要对象是缓冲区,操作主要有两个:读和写。读操作时,如果缓冲区为空,则从文件读取数据填满缓冲区;如果缓冲区不为空,则从缓冲区读取数据。写操作时,如果缓冲区未满,则将数据写入缓冲区;如果缓冲区已满,则将缓冲区的数据写入文件,并继续写入剩余的数据。

二、bufio的读操作

bufio的读操作主要通过bufio.Reader对象实现,它提供了多种读取数据的方法,如Read、ReadLine、ReadString等。

1. 使用bufio.Reader读取文件内容

下面是一个使用bufio.Reader读取文件内容的示例:

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("read file err:", err)
		return
	}
	defer file.Close() // 使用defer关闭文件

	// 创建bufio.Reader对象。借助bufio来读取文件内容
	// func NewReader(rd io.Reader) *Reader
	reader := bufio.NewReader(file)

	// 循环读取文件内容
	for {

		// 通过Read方法Read()读取文件
		//buf := make([]byte, 1024)
		//n, err := bufioReader.Read(buf)
		//fmt.Println("读取到了多少个字节:", n)
		//读取到的内容
		//fmt.Println("读取到的内容:",string(buf[:n]))

		//通过ReadString方法读取文件
		// func (b *Reader) ReadString(delim byte) (string, error)
		//参数是以什么作为分隔符。读取得到的是字符串
		//注意,最后一行后面要是没有换行符,读取不到
		str, err := reader.ReadString('\n') // 读取字符串,以换行符为分隔符
		if err == io.EOF {                  // 判断文件是否结尾
			break
		} else if err != nil { // 判断错误,打印错误
			fmt.Printf("read file failed, err:%v", err)
			break
		}
		//循环中逐行打印出读取的内容
		fmt.Printf("read string SuccessFull: %s\n", str) // 打印读取的文件内容
	}
}

在这里插入图片描述

在这个示例中,我们首先使用os.Open函数打开文件,然后创建一个bufio.Reader对象,通过循环调用ReadString方法读取文件内容,每次读取一行。当遇到文件结尾时,ReadString方法会返回一个io.EOF错误,我们据此退出循环。

2. 统计文件中字符、空格、数字和其他字符的数量

下面是一个统计文件中字符、空格、数字和其他字符数量的示例:

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

// CharCount 定义结构体
type CharCount struct {
	ChCount    int
	NumCount   int
	SpaceCount int
	OtherCount int
}

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("read file err:", err)
		return
	}
	defer file.Close() // 使用defer关闭文件

	// 创建bufio.Reader对象
	reader := bufio.NewReader(file)

	var count CharCount

	// 循环读取文件内容
	for {
		//str, err := reader.ReadString('\n') // 读取字符串,以换行符为分隔符
		str, _, err := reader.ReadLine() // 按行读取,最后一行没有换行符也能读到
		if err == io.EOF {               // 判断文件是否结尾
			break
		}
		if err != nil { // 判断错误,打印错误
			fmt.Printf("read file failed, err:%v", err)
			break
		}

		//runeArr := []rune(str) // 将字符串转为切片类型
		runeArr := str // 将字符串转为切片类型
		fmt.Println("得到的数据是", string(runeArr))
		for _, v := range runeArr { // 循环读取数组
			switch {
			case v >= 'a' && v <= 'z': // 判断是不是小写字母
				fallthrough
			case v >= 'A' && v <= 'Z': // 判断是不是大写字母
				count.ChCount++
			case v == ' ' || v == '\t': // 判断是不是空格
				count.SpaceCount++
			case v >= '0' && v <= '9': // 判断是不是数字
				count.NumCount++
			default:
				count.OtherCount++
			}
		}
	}

	fmt.Printf("char count:%d\n", count.ChCount)
	fmt.Printf("num count:%d\n", count.NumCount)
	fmt.Printf("space count:%d\n", count.SpaceCount)
	fmt.Printf("other count:%d\n", count.OtherCount)
}

在这里插入图片描述

3. 使用Scanner读取标准输入

下面是一个使用bufio.Scanner读取标准输入的示例:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 读取键盘的输入
	// 键盘的输入,实际上是流 os.Stdin
	inputReader := bufio.NewReader(os.Stdin)
	// delim 到哪里结束读取 以换行符作为分隔符,之言不换行就可以一直读
	readString, _ := inputReader.ReadString('\n')
	fmt.Println("读取键盘输入的信息:", readString)
}

在这里插入图片描述

三、bufio的写操作

bufio的写操作主要通过bufio.Writer对象实现,它提供了多种写入数据的方法,如Write、WriteString、Flush等。

1. 使用bufio.Writer写入文件内容

下面是一个使用bufio.Writer写入文件内容的示例:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 打开文件句柄,以写的方式打开,如果文件不存在则创建
	file, err := os.OpenFile("F:\\goworks\\src\\jingtian\\yufa\\io操作\\bufio操作\\test_go_file.log",
		os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("open file error:", err)
		return
	}
	defer file.Close() // 使用的defer语句关闭文件

	// 创建bufio.Writer对象
	fileWrite := bufio.NewWriter(file)

	inputStr := "Hello World ...\n"
	for i := 0; i < 10; i++ { // 循环写入数据
		fileWrite.WriteString(inputStr)
	}
	// 发现并没有写出到文件,是留在了缓冲区,所以我们需要调用 flush 刷新缓冲区
	// 必须手动刷新才能写入进文件
	fileWrite.Flush() // 刷新缓冲区,将数据写入文件
}

在这里插入图片描述

在这个示例中,我们首先使用os.OpenFile函数打开文件,然后创建一个bufio.Writer对象,通过循环调用WriteString方法写入文件内容。最后,我们调用Flush方法刷新缓冲区,将数据写入文件。

2. 自定义io.Writer对象进行缓冲写

下面是一个自定义io.Writer对象进行缓冲写的示例:

package main

import (
	"bufio"
	"fmt"
	"io"
	"strings"
)

// 自定义一个io.Writer对象
type StringWriter struct{}

func (s StringWriter) Write(p []byte) (n int, err error) {
	// 这里简单地将数据写入一个字符串变量(实际使用时,可以将其写入内存、文件等)
	fmt.Print(string(p))
	return len(p), nil
}

func main() {
	// 创建一个StringWriter对象
	sw := StringWriter{}

	// 创建一个bufio.Writer对象,并传入StringWriter对象
	writer := bufio.NewWriterSize(sw, 10)

	// 写入数据
	writer.WriteString("Hello, ")
	writer.WriteString("world!\n")

	// 刷新缓冲区,将数据写入StringWriter对象
	writer.Flush()
}

在这个示例中,我们自定义了一个StringWriter对象,它实现了io.Writer接口的Write方法。然后,我们创建一个bufio.Writer对象,并传入StringWriter对象。接着,我们调用WriteString方法写入数据,并调用Flush方法刷新缓冲区,将数据写入StringWriter对象。

四、bufio的缓冲区大小

在bufio包中,Reader和Writer对象都有默认的缓冲区大小,但你也可以根据需要自定义缓冲区大小。

1. 默认缓冲区大小

对于bufio.Readerbufio.Writer,Go语言标准库为它们提供了默认的缓冲区大小。对于bufio.Reader,默认缓冲区大小通常是4096字节(4KB);对于bufio.Writer,默认缓冲区大小也是4096字节(但在某些实现中可能略有不同,具体取决于底层操作系统的I/O性能)。

2. 自定义缓冲区大小

如果你需要更大的缓冲区来提高性能,或者更小的缓冲区来减少内存使用,你可以使用bufio.NewReaderSizebufio.NewWriterSize函数来创建具有自定义缓冲区大小的Reader和Writer对象。

下面是一个自定义缓冲区大小的示例:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 打开文件
	file, err := os.OpenFile("F:\\goworks\\src\\jingtian\\yufa\\io操作\\bufio操作\\custom_buffer_file.log", os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("open file error:", err)
		return
	}
	defer file.Close() // 使用defer语句关闭文件

	// 自定义缓冲区大小(例如:8KB)
	bufferSize := 8 * 1024

	// 创建具有自定义缓冲区大小的bufio.Writer对象
	fileWrite := bufio.NewWriterSize(file, bufferSize)

	inputStr := "This is a line with custom buffer size.\n"
	for i := 0; i < 10; i++ { // 循环写入数据
		_, err := fileWrite.WriteString(inputStr)
		if err != nil {
			fmt.Println("write file error:", err)
			return
		}
	}

	// 刷新缓冲区,将数据写入文件
	err = fileWrite.Flush()
	if err != nil {
		fmt.Println("flush buffer error:", err)
		return
	}

	fmt.Println("Data written to file successfully with custom buffer size.")
}

在这个示例中,我们创建了一个具有8KB缓冲区大小的bufio.Writer对象,并将其用于将数据写入文件。注意,在写入数据后,我们调用了Flush方法来确保缓冲区中的数据被写入文件。

对于bufio.Reader,你也可以使用类似的方法来创建具有自定义缓冲区大小的对象:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	// 打开文件
	file, err := os.Open("F:\\goworks\\src\\jingtian\\yufa\\io操作\\bufio操作\\custom_buffer_file.log")
	if err != nil {
		fmt.Println("read file error:", err)
		return
	}
	defer file.Close() // 使用defer语句关闭文件

	// 自定义缓冲区大小(例如:8KB)
	bufferSize := 8 * 1024

	// 创建具有自定义缓冲区大小的bufio.Reader对象
	fileRead := bufio.NewReaderSize(file, bufferSize)

	// 读取文件内容(这里只是简单地读取并打印每一行)
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			if err.Error() == "EOF" { // 判断是否到达文件末尾
				break
			}
			fmt.Println("read file error:", err)
			return
		}
		fmt.Print(line) // 打印读取到的行
	}

	fmt.Println("File read successfully with custom buffer size.")
}

在这个示例中,我们创建了一个具有8KB缓冲区大小的bufio.Reader对象,并将其用于读取文件内容。注意,在处理文件读取时,我们需要检查错误是否为EOF(文件末尾),以确定是否应该退出循环。

通过自定义缓冲区大小,你可以根据具体的应用场景和需求来优化bufio的性能和内存使用。

到此这篇关于Go语言中缓冲bufio的原理解读与应用实战的文章就介绍到这了,更多相关Go语言缓冲bufio内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang并发发送HTTP请求的各种方法

    Golang并发发送HTTP请求的各种方法

    在 Golang 领域,并发发送 HTTP 请求是优化 Web 应用程序的一项重要技能,本文探讨了实现此目的的各种方法,从基本的 goroutine 到涉及通道和sync.WaitGroup 的高级技术,需要的朋友可以参考下
    2024-02-02
  • 使用go备份StarRocks建表语句方法实例

    使用go备份StarRocks建表语句方法实例

    这篇文章主要为大家介绍了使用go备份StarRocks建表语句方法实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Go语言接口与多态详细介绍

    Go语言接口与多态详细介绍

    Go语言的接口类型是一组方法定义的集合,它体现了多态性、高内聚和低耦合的设计思想,接口通过interface关键字定义,无需实现具体方法,任何实现了接口所有方法的类型即视为实现了该接口,感兴趣的朋友一起看看吧
    2024-09-09
  • Go 实现 WebSockets之创建 WebSockets

    Go 实现 WebSockets之创建 WebSockets

    这篇文章主要介绍了Go 实现 WebSockets之创建 WebSockets,文章主要探索 WebSockets,并简要介绍了它们的工作原理,并仔细研究了全双工通信,想了解更多相关内容的小伙伴可以参考一下
    2022-04-04
  • Go语言实现布谷鸟过滤器的方法

    Go语言实现布谷鸟过滤器的方法

    这篇文章主要介绍了Go语言实现布谷鸟过滤器的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • 一文带你揭秘Go中new()和make()函数的区别和用途

    一文带你揭秘Go中new()和make()函数的区别和用途

    Go(或 Golang)是一种现代、静态类型、编译型的编程语言,专为构建可扩展、并发和高效的软件而设计,它提供了各种内置的函数和特性,帮助开发人员编写简洁高效的代码,在本博客文章中,我们将探讨 new() 和 make() 函数之间的区别,了解何时以及如何有效地使用它们
    2023-10-10
  • Golang中禁止拷贝的实现代码

    Golang中禁止拷贝的实现代码

    这篇文章主要给大家介绍了关于Golang中实现禁止拷贝的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • Golang多线程排序实现快速高效地处理大规模数据

    Golang多线程排序实现快速高效地处理大规模数据

    Golang多线程排序是一种快速高效地处理大规模数据的方法,通过使用Golang的协程和通道,可以将排序任务分配到多个线程中并行处理,提高了排序的效率和速度,需要详细了解可以参考下文
    2023-05-05
  • Golang关键字defer的用法详解

    Golang关键字defer的用法详解

    defer是Go里面的一个关键字,用在方法或函数前面,作为方法或函数的延迟调用。这篇文章主要为大家介绍了defer的简单使用,需要的可以参考一下
    2023-05-05
  • Golang连接PostgreSQL基本操作的实现

    Golang连接PostgreSQL基本操作的实现

    PostgreSQL是常见的免费的大型关系型数据库,本文主要介绍了Golang连接PostgreSQL基本操作的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02

最新评论