ZetCode

Go bufio

最后修改时间 2024 年 4 月 11 日

在本文中,我们将展示如何使用 bufio 包在 Golang 中执行缓冲的输入和输出操作。

bufio 包

内置的 bufio 包实现了缓冲的 IO 操作。缓冲是一种提高 IO 操作性能的技术。

由于系统调用开销很大,当我们累积数据到缓冲区进行读取或写入时,IO 操作的性能得到了极大的提高。这减少了所需的系统调用次数。

type Reader
type Writer
type Scanner

Reader 为 io.Reader 对象实现了缓冲。Writer 为 io.Writer 对象实现了缓冲。Scanner 为读取数据(如逐行文本文件)提供了一个方便的接口。

使用 bufio.NewReaderbufio.NewReaderSize 创建新读取器。

func NewReader(rd io.Reader) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader

NewReader 函数返回一个新读取器,其缓冲区具有默认大小。NewReaderSize 返回一个新读取器,其缓冲区至少具有指定的大小。

使用 bufio.NewWriterbufio.NewWriterSize 创建新写入器。

func NewWriter(w io.Writer) *Writer
func NewWriterSize(w io.Writer, size int) *Writer

NewWriter 函数返回一个新写入器,其缓冲区具有默认大小。NewWriterSize 返回一个新写入器,其缓冲区至少具有指定的大小。

使用 bufio.NewScanner 创建新扫描器。

func NewScanner(r io.Reader) *Scanner

NewScanner 返回一个新扫描器以从 r 读取。分隔符函数默认为 ScanLines

func (b *Writer) Flush() error

Flush 将任何缓冲数据写入底层 io.Writer。

Go Reader.ReadString

ReadString 读取直到输入中第一个给定分隔符的出现。

func (b *Reader) ReadString(delim byte) (string, error)

它返回一个包含到分隔符为止(包括分隔符)的数据的字符串。

main.go
package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
    "strings"
)

func main() {

    fmt.Print("Enter your name: ")

    r := bufio.NewReader(os.Stdin)

    name, err := r.ReadString('\n')

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Hello %s!\n", strings.TrimSpace(name))
}

使用 ReadString 函数,我们从用户那里读取输入,并将消息输出到控制台。

r := bufio.NewReader(os.Stdin)

我们从标准输入创建一个新的读取器。

name, err := r.ReadString('\n')

从用户那里读取一个字符串输入。

$ go run main.go
Enter your name: Jan
Hello Jan!

Go Writer.WriteString

WriteString 将字符串写入缓冲区。

func (b *Writer) WriteString(s string) (int, error)

它返回写入的字节数。

main.go
package main

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

func main() {

    data := []string{"an old falcon", "misty mountains",
        "a wise man", "a rainy morning"}

    f, err := os.Create("words.txt")

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    wr := bufio.NewWriter(f)

    for _, line := range data {

        wr.WriteString(line + "\n")
    }

    wr.Flush()

    fmt.Println("data written")
}

该示例使用 Writer.WriteString 将几个字符串写入文件。

wr := bufio.NewWriter(f)

创建一个新的写入器。默认缓冲区大小为 4KB。

for _, line := range data {

    wr.WriteString(line + "\n")
}

在 for 循环中,我们将数据写入缓冲区。

wr.Flush()

由于我们的数据小于默认的 4KB 缓冲区大小,我们必须调用 Flush 才能将数据实际写入文件。

使用 Scanner 逐行读取文件

在下一个示例中,我们将使用 Scanner 逐行读取文件。

words.txt
sky
nice
cup
cloud
forest
water
pond
lake
snow

这是 words.txt 文件。

main.go
package main

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

func main() {

    f, err := os.Open("words.txt")

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    scanner := bufio.NewScanner(f)

    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

该示例读取一个包含每行一个单词的小文件。

scanner := bufio.NewScanner(f)

使用 bufio.NewScanner 创建新扫描器。

for scanner.Scan() {
    fmt.Println(scanner.Text())
}

Scan 函数将 Scanner 前进到下一个标记,该标记随后可通过 BytesText 方法访问。默认情况下,该函数按行前进。

$ go run main.go
sky
nice
cup
cloud
forest
water
pond
lake
snow

从字符串中读取单词

在下面的示例中,我们使用 Scanner 从字符串中读取单词。

main.go
package main

import (
    "bufio"
    "fmt"
    "log"
    "strings"
)

func main() {

    words := []string{}

    data := "A foggy mountain.\nAn old falcon.\nA wise man."

    sc := bufio.NewScanner(strings.NewReader(data))

    sc.Split(bufio.ScanWords)

    n := 0

    for sc.Scan() {
        words = append(words, sc.Text())
        n++
    }

    if err := sc.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("# of words: %d\n", n)

    for _, word := range words {

        fmt.Println(word)
    }
}

strings.NewReader 从字符串返回一个新读取器。

sc.Split(bufio.ScanWords)

我们通过使用 Split 告诉扫描器按单词扫描。

Go Writer.WriteRune

WriteRune 写入单个 rune。

func (b *Writer) WriteRune(r rune) (size int, err error)

它返回写入的字节数和任何错误。

main.go
package main

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

func main() {

    runes := "🐜🐬🐄🐘🦂🐫🐑🦍🐯🐞"

    f, err := os.Create("runes.txt")

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    wr := bufio.NewWriter(f)

    for _, _rune := range runes {

        wr.WriteRune(_rune)
        wr.WriteRune('\n')
    }

    wr.Flush()

    fmt.Println("runes written")
}

在示例中,我们从字符串读取 rune 并将它们写入文件;每行一个。

Go Reader.Read

Reader.Read 函数将数据读取到字节切片中。

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

它返回读取的字节数。

在下一个示例中,我们还使用了 hex 包,它实现了十六进制编码和解码。

main.go
package main

import (
    "bufio"
    "encoding/hex"
    "fmt"
    "log"
    "os"
    "io"
)

func main() {

    f, err := os.Open("sid.jpg")

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    reader := bufio.NewReader(f)
    buf := make([]byte, 256)

    for {
        _, err := reader.Read(buf)

        if err != nil {

            if err != io.EOF {
                fmt.Println(err)
            }

            break
        }

        fmt.Printf("%s", hex.Dump(buf))
    }
}

在代码示例中,我们读取一个图像并以十六进制格式打印它。

reader := bufio.NewReader(f)

我们使用 bufio.NewReader 创建一个读取器。

buf := make([]byte, 256)

我们创建了一个 256 字节的自定义缓冲区。

for {
    _, err := reader.Read(buf)
...

我们在 for 循环中读取二进制数据。

fmt.Printf("%s", hex.Dump(buf))

Dump 返回一个包含给定数据十六进制转储的字符串。

$ go run main.go
00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 00 00 01  |......JFIF......|
00000010  00 01 00 00 ff e1 00 2f  45 78 69 66 00 00 49 49  |......./Exif..II|
00000020  2a 00 08 00 00 00 01 00  0e 01 02 00 0d 00 00 00  |*...............|
00000030  1a 00 00 00 00 00 00 00  6b 69 6e 6f 70 6f 69 73  |........kinopois|
00000040  6b 2e 72 75 00 ff fe 00  3b 43 52 45 41 54 4f 52  |k.ru....;CREATOR|
00000050  3a 20 67 64 2d 6a 70 65  67 20 76 31 2e 30 20 28  |: gd-jpeg v1.0 (|
00000060  75 73 69 6e 67 20 49 4a  47 20 4a 50 45 47 20 76  |using IJG JPEG v|
00000070  38 30 29 2c 20 71 75 61  6c 69 74 79 20 3d 20 39  |80), quality = 9|
00000080  31 0a ff db 00 43 00 03  02 02 03 02 02 03 03 02  |1....C..........|
00000090  03 03 03 03 03 04 07 05  04 04 04 04 09 06 07 05  |................|
000000a0  07 0a 09 0b 0b 0a 09 0a  0a 0c 0d 11 0e 0c 0c 10  |................|
000000b0  0c 0a 0a 0e 14 0f 10 11  12 13 13 13 0b 0e 14 16  |................|
...

来源

Go bufio 包 - 参考

在本文中,我们使用了 Go 中的 bufio 包。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面有十多年的经验。

列出所有 Go 教程