Go语言进阶篇——文件

简介: Go语言进阶篇——文件

文件的打开

文件的常见的两种打开方式是基于os包所提供的两个函数:

func Open(name string) (*File,error)
func OpenFile(name string flag int perm FileMode) (*File,error)

相对于前者,OpenFile可以提供更加细致的操作,而前者就是对后者的一个简单封装

我们首先来看第一种使用方法,我们只需要提供对应的文件名就可以了,代码如下:

func main() {
  file, err := os.Open("test.txt")
  defer file.Close()
  if err != nil {
    fmt.Println("文件访问异常")
    return
  }
}

文件的查找路径默认为项目go.mod文件所在的路径,由于项目下并没有该文件,所以自然会返回一个错误。

因为IO错误的类型有很多,所以有时后需要我们去手动的去判断文件是否存在,而os包也为此提供了方便函数,修改后的函数如下:

func main() {
  file, err := os.Open("test.txt")
  defer file.Close()
  if os.IsNotExist(err) {
    fmt.Println("file not found")
  } else if err != nil {
    fmt.Println("error")
  } else {
    fmt.Println("file found")
  }
}

事实上第一种函数读取的文件仅仅只是只读的,无法被修改,Open函数内部实现

func Open(name string) (*File, error) {
  return OpenFile(name, O_RDONLY, 0)
}

通过OpenFile函数可以控制更多细节,例如修改文件描述符和文件权限,关于文件描述符,os包下提供了以下常量以供使用。

解释const (
   // 只读,只写,读写 三种必须指定一个
   O_RDONLY int = syscall.O_RDONLY // 以只读的模式打开文件
   O_WRONLY int = syscall.O_WRONLY // 以只写的模式打开文件
   O_RDWR   int = syscall.O_RDWR   // 以读写的模式打开文件
   // 剩余的值用于控制行为
   O_APPEND int = syscall.O_APPEND // 当写入文件时,将数据添加到文件末尾
   O_CREATE int = syscall.O_CREAT  // 如果文件不存在则创建文件
   O_EXCL   int = syscall.O_EXCL   // 与O_CREATE一起使用, 文件必须不存在
   O_SYNC   int = syscall.O_SYNC   // 以同步IO的方式打开文件
   O_TRUNC  int = syscall.O_TRUNC  // 当打开的时候截断可写的文件
)

关于文件权限的则提供了以下常量。

解释const (
   ModeDir        = fs.ModeDir        // d: 目录
   ModeAppend     = fs.ModeAppend     // a: 只能添加
   ModeExclusive  = fs.ModeExclusive  // l: 专用
   ModeTemporary  = fs.ModeTemporary  // T: 临时文件
   ModeSymlink    = fs.ModeSymlink    // L: 符号链接
   ModeDevice     = fs.ModeDevice     // D: 设备文件
   ModeNamedPipe  = fs.ModeNamedPipe  // p: 具名管道 (FIFO)
   ModeSocket     = fs.ModeSocket     // S: Unix 域套接字
   ModeSetuid     = fs.ModeSetuid     // u: setuid
   ModeSetgid     = fs.ModeSetgid     // g: setgid
   ModeCharDevice = fs.ModeCharDevice // c: Unix 字符设备, 前提是设置了 ModeDevice
   ModeSticky     = fs.ModeSticky     // t: 黏滞位
   ModeIrregular  = fs.ModeIrregular  // ?: 非常规文件
   // 类型位的掩码. 对于常规文件而言,什么都不会设置.
   ModeType = fs.ModeType
   ModePerm = fs.ModePerm // Unix 权限位, 0o777
)

我们下面可以实现一个以读写模式打开一个文件的代码例子,权限为0666,表示为所有人都可以对该文件进行读写,且不存在时会自动创建。

func main() {
  file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0666)
  if os.IsNotExist(err) {
    fmt.Println("文件不存在")
  } else if err != nil {
    fmt.Println("文件打开有异常")
  } else {
    fmt.Println("文件打开成功", file.Name())
    defer file.Close()
  }
}

文件的读取

常见的文件读取

当我们成功打开文件以后,我们就可以开始进行读取操作了,对于读取文件的操作,os.file提供了以下几个公开的方法

// 将文件读进传入的字节切片
func (f *File) Read(b []byte) (n int, err error) 
// 相较于第一种可以从指定偏移量读取
func (f *File) ReadAt(b []byte, off int64) (n int, err error) func 

大部分情况下第一种情况使用的较多,针对第一种方法,我们需要自己编写逻辑来进行读取时切片的动态扩容,代码如下:

func ReadFile(f *os.File) ([]byte, error) {
  buffer := make([]byte, 0, 512)
  for {
    if len(buffer) == cap(buffer) {
      //扩容
      buffer = append(buffer, 0)[:len(buffer)]
    }
    //继续读取
    offerset, err := f.Read(buffer[len(buffer):cap(buffer)])
    buffer = buffer[:len(buffer)+offerset]
    // 发生错误时
    if err != nil {
      if errors.Is(err, io.EOF) {
        err = nil
      }
      return buffer, err
    }
  }
}

剩余逻辑为:

func main() {
  file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0666)
  if err != nil {
    fmt.Println("文件打开异常")
  } else {
    fmt.Println("文件打开成功", file.Name())
  }
  bytes, err := ReadFile(file)
  if err != nil {
    fmt.Println("文件读取异常")
  } else {
    fmt.Println("文件读取成功", bytes)
  }
  file.Close()
}

除此之外,我们还可以使用两个方便函数来进行文件读取,分别是os包下的ReadFile函数以及io包下的ReadAll函数,相对于前者而言,我们只需要提供文件路径即可,而后者我们则需要提供一个io.Raeder类型的实现。

os.ReadFile

函数形式

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

使用例子:

func main() {
  bytes, err := os.ReadFile("README.txt")
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println(string(bytes))
  }
}

io.ReadAll

函数形式:

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

示例:

func main() {
   file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE, 0666)
   if err != nil {
      fmt.Println("文件访问异常")
   } else {
      fmt.Println("文件打开成功", file.Name())
      bytes, err := io.ReadAll(file)
      if err != nil {
         fmt.Println(err)
      } else {
         fmt.Println(string(bytes))
      }
      file.Close()
   }
}

文件的写入

os.File结构体提供以下几种方法来供我们写入数据:

//写入字节切片
func (f *file) Write(b []byte) (int,error)
//写入字符串
func (f *file) WriteString(s string) (int,error)
 从指定位置开始写,当以os.O_APPEND模式打开时,会返回错误
func (f *File) WriteAt(b []byte, off int64) (n int, err error)

如果我们要对文件写入数据。我们需要以O_WRONLYO_RDWR

模式打开文件,否则无法写入,接下来我们来看一个示例:

func main() {
  file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)
  if err != nil {
    fmt.Println("文件访问异常")
  } else {
    fmt.Println("文件打开成功", file.Name())
    for i := 0; i < 5; i++ {
      offset, err := file.WriteString("hello world!\n")
      if err != nil {
        fmt.Println(offset, err)
      }
    }
    fmt.Println(file.Close())
  }
}

向文件写入字节切片也是类似的操作,就不再赘述。对于写入文件的操作标准库同样提供了方便函数,分别是os.WriteFileio.WriteString

os.WriteFile

func WriteFile(name string, data []byte, perm FileMode) error
• 1

使用例子如下

func main() {
  err := os.WriteFile("README.txt", []byte("hello world!\n"), 0666)
  if err != nil {
    fmt.Println(err)
  }
}

此时文件内容如下

hello world!

io.WriteString

func WriteString(w Writer, s string) (n int, err error) 

使用例子如下

func main() {
   file, err := os.OpenFile("README.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0666)
   if err != nil {
      fmt.Println("文件访问异常")
   } else {
      fmt.Println("文件打开成功", file.Name())
      for i := 0; i < 5; i++ {
         offset, err := io.WriteString(file, "hello world!\n")
         if err != nil {
            fmt.Println(offset, err)
         }
      }
      fmt.Println(file.Close())
   }
}

文件的复制

对于复制文件而言,需要同时打开两个文件,第一种方法是将原文件中的数据读取出来,然后写入目标文件中,代码示例如下

解释func main() {
    // 从原文件中读取数据
  data, err := os.ReadFile("README.txt")
  if err != nil {
    fmt.Println(err)
    return
  }
    // 写入目标文件
  err = os.WriteFile("README(1).txt", data, 0666)
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println("复制成功")
  }
}

os.File.ReadFrom

另一种方法是使用os.File提供的方法ReadFrom,打开文件时,一个只读,一个只写。

func (f *File) ReadFrom(r io.Reader) (n int64, err error)
• 1

使用示例如下

解释func main() {
  // 以只读的方式打开原文件
  origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer origin.Close()
  // 以只写的方式打开副本文件
  target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer target.Close()
  // 从原文件中读取数据,然后写入副本文件
  offset, err := target.ReadFrom(origin)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println("文件复制成功", offset)
}

io.Copy

还有一种方法就是使用io.Copy方便函数

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

使用示例如下

解释func main() {
  // 以只读的方式打开原文件
  origin, err := os.OpenFile("README.txt", os.O_RDONLY, 0666)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer origin.Close()
  // 以只写的方式打开副本文件
  target, err := os.OpenFile("README(1).txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  if err != nil {
    fmt.Println(err)
    return
  }
  defer target.Close()
  // 复制
  written, err := io.Copy(target, origin)
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println(written)
  }
}

文件重命名

重命名也可以理解为移动文件,会用到os包下的Rename函数。

func Rename(oldpath, newpath string) error
• 1

示例如下

解释func main() {
  err := os.Rename("README.txt", "readme.txt")
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println("重命名成功")
  }
}

**注意:**重命名文件夹,移动文件夹同样适用。

文件的删除

删除操作相较于其他操作要简单的多,只会用到os包下的两个函数

解释// 删除单个文件或者空目录,当目录不为空时会返回错误
func Remove(name string) error
// 删除指定目录的所有文件和目录包括子目录与子文件
func RemoveAll(path string) error 

使用起来十分的简单,下面是删除目录的例子

解释func main() {
  // 删除当前目录下所有的文件与子目录
  err := os.RemoveAll(".")
  if err != nil {
    fmt.Println(err)
  }else {
    fmt.Println("删除成功")
  }
}

下面删除单个文件的例子

相关文章
|
3天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
16 4
|
3天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
27 1
|
6天前
|
Go
go语言中的continue 语句
go语言中的continue 语句
16 3
|
7天前
|
安全 Go 调度
探索Go语言的并发模型:goroutine与channel
在这个快节奏的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你深入了解Go语言的goroutine和channel,这两个核心特性如何协同工作,以实现高效、简洁的并发编程。
|
8天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
17 4
|
8天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
40 1
|
1天前
|
存储 Go PHP
Go语言中的加解密利器:go-crypto库全解析
在软件开发中,数据安全和隐私保护至关重要。`go-crypto` 是一个专为 Golang 设计的加密解密工具库,支持 AES 和 RSA 等加密算法,帮助开发者轻松实现数据的加密和解密,保障数据传输和存储的安全性。本文将详细介绍 `go-crypto` 的安装、特性及应用实例。
11 0
|
10天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
30 2
golang操作文件
1、读取文件信息: /* 读取文件信息 */ func readFile(path string) string { fi, err := os.Open(path) if err != nil { panic(err) } defer fi.
1402 0
下一篇
无影云桌面