> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/golang/wen-jian-cao-zuo.md).

# 文件操作

`os` 包提供了平台无关的操作系统功能接口。尽管错误处理是 go 风格的，但设计是 Unix 风格的；所以，失败的调用会返回 `error` 而非错误码。通常 `error` 里会包含更多信息。例如，如果使用一个文件名的调用（如 Open、Stat）失败了，打印错误时会包含该文件名，错误类型将为 `*PathError`，其内部可以解包获得更多信息。

os 包规定为所有操作系统实现的接口都是一致的。有一些某个系统特定的功能，需要使用 `syscall` 获取。实际上，`os` 依赖于 `syscall`。在实际编程中，我们应该总是优先使用 `os` 中提供的功能，而不是 `syscall`。

## 带缓冲读取

```go
package main

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

func main() {
    file, err := os.Open("/Users/yangsx/Desktop/helloworld.txt") // 返回一个文件对象/文件指针/文件句柄
    defer file.Close() // 关闭File句柄

    if err != nil {
        fmt.Println("open file err: ", err)
    }

    // 创建一个*Reader，默认带大小4096字节的缓冲区
    reader := bufio.NewReader(file)
    // 循环读取文件内容
    for {
        str, err := reader.ReadString('\n') // 读取到换行符就结束
        if err == io.EOF { // 如果读取到文件末尾
            break
        }
        // 输出该行
        fmt.Print(str) // 会把换行符打印
    }
    fmt.Println("----文件读取结束----")
}
```

## 一次全文读取

> 1.16后，`ioutil`实际上调用`os.ReadFile()`函数，推荐直接使用`os.ReadFile()`函数

```go
package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    // 返回字节数组
    bs, err := ioutil.ReadFile("/Users/yangsx/Desktop/helloworld.txt")
    if err != nil {
        fmt.Println("读取文件失败：", err)
    }
    fmt.Printf("%s", bs)
}
```

## 打开文件的方式

使用`func OpenFile(name string, flag int, perm FileMode) (file *File, err error)`函数打开文件并指定打开模式：

```go
// flag，打开方式，多个可以组合使用  os.O_RDWR | os.APPEND
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   // 打开文件用于同步I/O
    O_TRUNC  int = syscall.O_TRUNC  // 如果可能，打开时清空文件
)

// 文件权限
const (
    // 单字符是被String方法用于格式化的属性缩写。
    ModeDir        FileMode = 1 << (32 - 1 - iota) // d: 目录
    ModeAppend                                     // a: 只能写入，且只能写入到末尾
    ModeExclusive                                  // l: 用于执行
    ModeTemporary                                  // T: 临时文件（非备份文件）
    ModeSymlink                                    // L: 符号链接（不是快捷方式文件）
    ModeDevice                                     // D: 设备
    ModeNamedPipe                                  // p: 命名管道（FIFO）
    ModeSocket                                     // S: Unix域socket
    ModeSetuid                                     // u: 表示文件具有其创建者用户id权限
    ModeSetgid                                     // g: 表示文件具有其创建者组id的权限
    ModeCharDevice                                 // c: 字符设备，需已设置ModeDevice
    ModeSticky                                     // t: 只有root/创建者能删除/移动文件
    // 覆盖所有类型位（用于通过&获取类型位），对普通文件，所有这些位都不应被设置
    ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
    ModePerm FileMode = 0777 // 覆盖所有Unix权限位（用于通过&获取类型位）
)
```

## 一次性写入

> 实际调用`os.WriteFile()`函数

```go
package main

import (
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    str := "hello"
    // 覆盖写入一次性写入字符串
    err := ioutil.WriteFile("/Users/yangsx/Desktop/helloworld.txt", []byte(str), 0666)
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println("写入成功")
}
```

## 带缓冲追加写入

```go
package main

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

func main() {
    file, err := os.OpenFile("/Users/yangsx/Desktop/helloworld.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    defer file.Close()
    if err != nil {
        fmt.Println("打开文件出错：", err)
    }
    // 写入数据
    str := "你好，世界\n"
    // 使用带缓存的 *Writer
    writer := bufio.NewWriter(file)
    for i := 0; i < 5; i++ {
        writer.WriteString(str)
    }
    // 因为writer是带缓存的，需要使用flush函数真正写入到磁盘中
    writer.Flush()

    fmt.Println("写入数据成功")
}
```

## 覆盖写入

使用选项， `os.O_TRUNC` 清空文件

## 判断文件状态

**判断文件/文件夹是否存在：**

```go
// Stat函数就像linux下的stat命令，该函数会返回一个FileInfo struct
func PathExist(path string) (bool, error){
    _, err := os.Stat(path)
    if err == nil {
        return true, nil // 没有错误，说明文件/文件夹一定存在
    }
    if os.IsNotExist(err) { // 如果错误信息是文件不存在，则说明文件/文件夹一定不存在
        return false, nil
    }
    return false, err
}
```

**获取文件状态：**

```go
package main

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

func main() {
    fi, err := os.Stat("/Users/yangsx/Desktop/helloworld.txt")
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Printf(`文件名称为：%s, 
文件大小为： %d,
是否是文件夹： %t,
文件的Mode： %d
`, fi.Name(), fi.Size(), fi.IsDir(), fi.Mode())
}
```

## 文件拷贝

```go
package main

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

func main() {
    src := "/Users/yangsx/Desktop/helloworld.txt"
    dst := "/Users/yangsx/Desktop/h.txt"

    srcF, err := os.OpenFile(src, os.O_RDONLY, 0666)
    defer srcF.Close()
    if err != nil {
        fmt.Println("打开src失败： ", err)
        return
    }
    dstF, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    defer dstF.Close()
    if err != nil {
        fmt.Println("打开dst失败： ", err)
        return
    }

    // 构建reader 和 writter
    reader := bufio.NewReader(srcF)
    writer := bufio.NewWriter(dstF)

    // 调用拷贝方法
    wi, err := io.Copy(writer, reader)
    if err != nil {
        fmt.Println("拷贝失败 ", err)
        return
    }
    fmt.Println("拷贝成功，字节数：", wi)
}
```
