文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文带你完整了解Go语言IO基础库

2024-11-30 00:51

关注

第一部分定义了最基本的流操作接口,包括Writer, Reader, Seeker, Closer这几个以及相关的组合接口。分别表达写入,读取, 偏移读和关闭操作处理。

全局类图以及关系如下,方便大家更直观的理解:

图片

以下对接口进行了源码摘取并进行中文注释:


type Writer interface {
    Write(p []byte) (n int, err error)
}

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}

type Closer interface {
    Close() error
}

在基本的接口外, io库还提供了一些扩展的读写处理能力的接口定义,以提升更便捷的使用:

全局类图以及关系如下, 方便大家更直观的理解:

图片

以下对接口进行了源码摘取并进行中文注释:


type ReaderAt interface {
    ReadAt(p []byte, off int64) (n int, err error)
}

type RuneReader interface {
    ReadRune() (r rune, size int, err error)
}

type ByteReader interface {
    ReadByte() (byte, error)
}

type ReaderFrom interface {
    ReadFrom(r Reader) (n int64, err error)
}

全局类图以及关系如下, 方便大家更直观的理解:

图片

以下对接口进行了源码摘取并进行中文注释:


type WriterAt interface {
    WriteAt(p []byte, off int64) (n int, err error)
}

type WriterTo interface {
    WriteTo(w Writer) (n int64, err error)
}

type StringWriter interface {
    WriteString(s string) (n int, err error)
}

io库还提供了很实用的工具方法,整理如下:

Copy:

func Copy(dst Writer, src Reader) (written int64, err error)
△注:将副本从 src 复制到 dst,直到 src 达到 EOF 或发生错误。它返回复制的字节数以及复制时遇到的第一个错误(如果有)。

成功的 Copy 返回 err == nil,而不是 err == EOF。因为 Copy 被定义为从 src 读取直到 EOF,所以它不会将 Read 中的 EOF 视为要报告的错误。

如果 src 实现WriterTo,则通过调用 src.WriteTo(dst) 实现复制。否则,如果 dst 实现了ReaderFrom,则通过调用 dst.ReadFrom(src) 来实现复制。

CopyBuffer:

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
△注:CopyBuffer 与 Copy 相同,只是它分阶段遍历提供的缓冲区(如果需要)而不是分配临时缓冲区。如果 buf 为 nil,则分配 1;否则,如果它的长度为零,CopyBuffer 就会出现混乱。

如果 src 实现WriterTo或 dst 实现ReaderFrom,则 buf 将不会用于执行复制。

CopyN:

func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
△注:CopyN 将 n 个字节(或直到出现错误)从 src 复制到 dst。它返回复制的字节数以及复制时遇到的最早错误。返回时,当且仅当 err == nil 时写为 == n。

如果 dst 实现ReaderFrom,则使用它来实现副本。

Pipe:

func Pipe() (*PipeReader, *PipeWriter)
△注:Pipe 创建同步内存管道。它可用于将需要io.Reader的代码 与需要io.Writer 的代码连接起来。

管道上的读取和写入是一对一匹配的,除非需要多个读取来消耗单个写入。也就是说,对 PipeWriter 的每次写入都会阻塞,直到满足来自PipeReader的一次或多次读取(完全消耗写入数据)为止。数据直接从Write复制到对应的Read(或Reads);没有内部缓冲。

并行调用 Read 和 Write 或与 Close 一起调用是安全的。对 Read 的并行调用和对 Write 的并行调用也是安全的:各个调用将按顺序进行门控。

ReadAll:

func ReadAll(r Reader) ([]byte, error)
△注:ReadAll 从 r 读取直到出现错误或 EOF,然后返回读取的数据。成功地调用返回 err == nil,而不是 err == EOF。因为 ReadAll 被定义为从 src 读取直到 EOF,所以它不会将 Read 中的 EOF 视为要报告的错误。ReadAtLeast:
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
△注:ReadAtLeast 从 r 读取到 buf 中,直到读取至少 min 个字节。它返回复制的字节数,如果读取的字节数较少,则返回错误。仅当未读取任何字节时,错误才为 EOF。如果读取少于 min 字节后发生 EOF,则 ReadAtLeast 返回ErrUnexpectedEOF。如果 min 大于 buf 的长度,则 ReadAtLeast 返回ErrShortBuffer。返回时,n >= min 当且仅当 err == nil 时。如果 r 在读取了至少 min 个字节后返回错误,则该错误将被丢弃。

ReadFull:

func ReadFull(r Reader, buf []byte) (n int, err error)
△注:ReadFull 将 r 中的 len(buf) 个字节准确读取到 buf 中。它返回复制的字节数,如果读取的字节数较少,则返回错误。仅当未读取任何字节时,错误才为 EOF。如果在读取部分字节但不是全部字节后发生 EOF,则 ReadFull 返回ErrUnexpectedEOF。返回时,n == len(buf) 当且仅当 err == nil 时。如果 r 在读取至少 len(buf) 个字节后返回错误,则该错误将被丢弃。

WriteString:

func WriteString(w Writer , s string ) (n int , err error)
△注:WriteString 将字符串 s 的内容写入 w,它接受字节切片。如果 w 实现StringWriter,则直接调用 [StringWriter.WriteString] 。否则,[Writer.Write] 只会被调用一次。

文件操作读写示例:

// ReadFileExample 读取文件内容并输出
func ReadFileExample() {
    // 打开文件,第一个参数是文件路径,第二个参数是文件打开模式
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer file.Close() // 延迟关闭文件,确保文件在函数执行完毕后被关闭


    // 读取文件内容
    data := make([]byte, 100) // 读取数据的缓冲区
    count, err := file.Read(data)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }


    // 输出文件内容
    fmt.Printf("Read %d bytes: %s\n", count, data[:count])
}


// WriteFileExample 函数演示如何写入数据到文件中
func WriteFileExample() {
    // 创建文件,第一个参数是文件路径,如果文件已存在则会被截断清空
    file, err := os.Create("example.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer file.Close() // 延迟关闭文件,确保文件在函数执行完毕后被关闭


    // 写入数据到文件
    data := []byte("Hello, world!\n")
    _, err = file.Write(data)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }


    fmt.Println("Data has been written to output.txt")
}

io库的第二部分,定义了一个子包"fs", 定义了文件操作相关的接口,包括 File, FS, DirEntry等。

全局类图以及关系如下, 方便大家更直观的理解:

图片

以下对接口进行了源码摘取并进行中文注释:


type File interface {
    Stat() (FileInfo, error)
    Read([]byte) (int, error)
    Close() error
}

type FileInfo interface {
    Name() string        // 文件的基本名称
    Size() int64         // 常规文件的长度(以字节为单位); 其他系统相关
    Mode() FileMode      // 文件模式位
    ModTime() time . Time  // 修改时间
    IsDir() bool         // Mode().IsDir() 的缩写
    Sys() any            // 底层数据源(可以返回 nil)
 }

type FS interface {
    // Open 打开指定的文件。
    // 
    // 当 Open 返回错误时,错误类型应为 *PathError 
    // Op 字段设置为“open”,Path 字段设置为 name,
    // 以及 Err 字段描述问题。
    // 
    // Open 应拒绝打开不满足// ValidPath(name) 的名称的尝试,返回 *PathError,并将 Err 设置为
    // ErrInvalid 或 ErrNotExist。
    Open(name string) (File, error)
}

type DirEntry interface {
     // Name 返回条目描述的文件(或子目录)的名称。
     // 这个名称只是路径的最后一个元素(基本名称),而不是整个路径。
     // 例如,Name 将返回“hello.go”而不是“home/gopher/hello.go”。
    Name() string


    // IsDir 报告条目是否描述目录
    IsDir() bool


    // Type 返回条目的类型位。
    // 类型位是通常 FileMode 位的子集,由 FileMode.Type 方法返回
    Type() FileMode


    // Info 返回条目描述的文件或子目录的 FileInfo。
    // 返回的 FileInfo 可能来自读取原始目录的时间
    // 或来自调用 Info. //如果自目录读取后文件已被删除或重命名,Info 可能会返回满足errors.Is(err, ErrNotExist) 的错误。
    // 如果条目表示符号链接,则 Info 报告有关链接本身的信息,
    // 而不是链接目标的信息。
    Info() (FileInfo, error)
}

2.OS库

至此io库的部分已经介绍结束,但应该有同学会问, 如何使用这些库,特别是文件操作?那就要是与os库联合使用了。 下面也针对os库进行了整理,并给出了相关的示例,方便大家掌握。

全局类图以及关系如下,方便大家更直观的理解:

图片

类图上,可以看到os库下也定义了File对象,与fs.File接口一样,有一个Stat方法,但返回值变成了os.FileInfo,但类型是直接使用了fs.FileInfo。

// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo = fs.FileInfo


// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably. Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode = fs.FileMode

以下是最简单的文件操作示例:

file, err := os.Open("example.txt")
  if err != nil {
      fmt.Println("无法打开文件:", err)
      return
  }
  defer file.Close() // 确保在函数退出时关闭文件


  bs := make([]byte, s.Size())
  file.Read(bs)
  // 打印文件内容
  log.Println(string(bs))

以下是最简单的使用fs.FS操作目录的示例:

root := "/usr/local/go/bin"
  fileSystem := os.DirFS(root) // 返回 fs.FS

所以总结来讲, os下的File是一个独立的实现,虽然不是直接实现了fs.File接口,但是操作行为,依赖的操作与fs包下的是完全一致的类型。

GEEK TALK

3.http包

接下来再来整理一下 http包下的文件相关的定义:

图片

http包下也定义了,FileSystem与File对象,进行相应的操作处理, 这一块的使用也比较好掌握,参见一下下面的示例。

示例:使用http.FileServer实现静态文件服务发布, 使用了 http.FileSystem

root := "/local/xxx/static"
  rootfs := os.DirFS(root) // 返回 fs.FS
  fsstatic := http.FileServer(http.FS(rootfs))
  // 设置路由
  http.Handle("/static/", http.StripPrefix("/static/", fsstatic))

4.embed包

最后再介绍一下 embed包。Golang1.16 版本引入的embed标准库,  支持把外部资源文件或目录直接在编译阶段打进编译包中,实现了应用打包时只需一个可执行包的效果。

embed支持把外部资源以 string, []byte或embed.FS方式使用。下面是几个使用示例:

//go:embed hello.txt
var s string


//go:embed hello.txt
var b []byte


//go:embed hello.txt
var f embed.FS

这里可以看到 embed也定义了 FS对象,用于FileSystem的操作处理。

图片

从上面的类图可以看到,embed.FS提供的读取文件内容,打开文件以及读取文件目录的功能。

以下对相关的方法进行了源码摘取并进行中文注释:


func (f FS) Open(name string) (fs.File, error)

func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

func (f FS) ReadFile(name string) ([]byte, error)

示例代码:从embed.FS读取文件目录,发布成http静态资源服务

package main


import (
    "embed"
    "log"
    "net/http"
)


//go:embed static/*
var staticFiles embed.FS


func main() {
    // 创建文件服务器
    fileServer := http.FileServer(http.FS(staticFiles))


    // 设置路由
    http.Handle("/static/", http.StripPrefix("/static/", fileServer))


    // 启动HTTP服务器
    log.Println("Server started on: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

GEEK TALK

5.总结

Go语言的基础库里,针对文件操作这一块,各个包都有自己的File, FS的定义,这给很多刚开始学习的同学带来了不少困惑,个人也是觉得设计上是有改进的空间的。希望上述的整理内容,可以帮助到大家更好的理解Go语言IO库的使用。

来源:百度Geek说内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯