数据IO操作

Go语言中,io包提供了基本的输入输出接口和函数,它是Go语言中处理数据流的核心包之一。无论是文件、网络通信还是内存缓冲区的读写,我们都可能会使用到io包。这篇笔记我们介绍io包中的一些常用接口和函数。

接口

io.Reader接口

io.Reader接口用于读取数据。

type Reader interface {
    Read(p []byte) (n int, err error)
}
  • p:存储读取数据的缓冲区
  • n:实际读取的字节数
  • err:读取过程中发生的错误

下面例子我们从一个文件中读取文本。

package main

import (
    "fmt"
    "os"
)

func main() {
    // 打开源文件
    file, err := os.Open("C:\\Users\\HUAWEI\\src.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭源文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
        }
    }(file)
    // 从文件读取部分字节数据
    buffer := make([]byte, 1024)
    n, err := file.Read(buffer)
    if err != nil {
        fmt.Println(err)
    }
    // 打印读取的数据
    fmt.Println(string(buffer[:n]))
}

os.Open()函数返回的file其实就实现了io.Reader接口,我们可以从其中读取数据。代码中,我们创建了一个1024字节的切片,并调用Read()方法从文件向其中读入数据,读取的数据字节数为n。最后,我们将读取的数据转换为字符串并打印出来。

出于简单起见,上面代码最多仅会读取1024字节的数据,实际开发中,如果需要读取更多的数据,我们需要创建一个循环来调用Read()方法。

注意:Go语言默认使用UTF-8编码实现字节和字符串之间的转换。

io.Writer接口

io.Writer接口用于写数据。

type Writer interface {
    Write(p []byte) (n int, err error)
}
  • p:要写入的数据
  • n:实际写入字节数
  • err:写入过程中发生的错误

下面是一个例子。

package main

import (
    "fmt"
    "os"
)

func main() {
    // 打开目标文件
    file, err := os.OpenFile("C:\\Users\\HUAWEI\\target.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭目标文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
            return
        }
    }(file)
    // 向文件写入数据
    dataStr := "Hello, world!"
    if _, err := file.Write([]byte(dataStr)); err != nil {
        fmt.Println(err)
        return
    }
}

代码中,os.OpenFile()函数返回的file实现了io.Writer接口,我们打开了文件并向其中写入一个字符串。

io.Closer接口

type Closer interface {
    Close() error
}

上面代码中的file其实也都实现了io.Closer接口,用于关闭文件并释放相关的资源。

defer func(file *os.File) {
    if err := file.Close(); err != nil {
        fmt.Println(err)
        return
    }
}(file)

io.Seeker接口

io.Seeker接口允许在数据流中移动读取和写入的位置。

type Seeker interface {
    Seek(offset int64, whence int) (ret int64, err error)
}
  • offset:偏移量(字节数)
  • whence:从哪里开始计算偏移量,0 文件开头,1 当前位置,2 文件结尾
  • ret:新的偏移位置
  • err:处理过程中发生的错误

下面例子代码我们从文件开头偏移15字节读取数据。

package main

import (
    "fmt"
    "os"
)

func main() {
    // 打开源文件
    file, err := os.Open("C:\\Users\\HUAWEI\\src.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭源文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
        }
    }(file)
    // 移动读取位置
    if _, err := file.Seek(15, 0); err != nil {
        fmt.Println(err)
    }
    // 从文件读取部分字节数据
    buffer := make([]byte, 1024)
    n, err := file.Read(buffer)
    if err != nil {
        fmt.Println(err)
    }
    // 打印读取的数据
    fmt.Println(string(buffer[:n]))
}

工具函数

io.Copy()函数

io.Copy()用于直接从Reader中复制所有数据到Writer,常用于文件复制,它的内部实际上有一个循环,该函数会阻塞直到Reader返回EOF或发生错误。

func Copy(dst Writer, src Reader) (written int64, err error)

下面是一个例子代码,它实现了文件复制。

package main

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

func main() {
    // 打开源文件
    srcFile, err := os.Open("C:\\Users\\HUAWEI\\src.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭源文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
        }
    }(srcFile)
    // 打开目标文件
    targetFile, err := os.OpenFile("C:\\Users\\HUAWEI\\target.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭目标文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
        }
    }(targetFile)
    // 复制文件
    written, err := io.Copy(targetFile, srcFile)
    fmt.Printf("%v bytes copied\n", written)
}

io.CopyN()函数

io.CopyN()io.Copy()类似,不过它最多会复制n个字节的数据。

func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

下面是一个例子。

// 复制文件
written, err := io.CopyN(targetFile, srcFile, 15)
fmt.Printf("%v bytes copied\n", written)

io.ReadAll()函数

io.ReadAll()用于读取Reader中的所有数据,返回一个[]byte切片。它的内部也是一个循环结构,会阻塞读取直到Reader返回EOF或发生错误。

func ReadAll(r Reader) ([]byte, error)

下面例子使用io.ReadAll()函数读取文件全部内容。

package main

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

func main() {
    // 打开源文件
    file, err := os.Open("C:\\Users\\HUAWEI\\src.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭源文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
            return
        }
    }(file)
    // 全部读取
    data, err := io.ReadAll(file)
    if err != nil {
        fmt.Println(err)
        return
    }
    // 打印读取的数据
    fmt.Println(string(data))
}

io.ReadFull()函数

io.ReadFull()函数用于读取Reader中的数据直到len(buffer)个字节的数据,也就是我们需要阻塞等待读取直到填满缓冲区数组,如果读取期间Reader返回EOF,则io.ReadFull()函数返回io.ErrUnexpectedEOF

func ReadFull(r Reader, buf []byte) (n int, err error)

下面是一个例子,代码中我们读取文件的前15个字节。

package main

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

func main() {
    // 打开源文件
    file, err := os.Open("C:\\Users\\HUAWEI\\src.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // 关闭源文件逻辑
    defer func(file *os.File) {
        if err := file.Close(); err != nil {
            fmt.Println(err)
            return
        }
    }(file)
    // 读取15个字节
    buffer := make([]byte, 15)
    if _, err := io.ReadFull(file, buffer); err != nil {
        fmt.Println(err)
        return
    }
    // 打印读取的数据
    fmt.Println(string(buffer))
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap