# 文件操作

`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)
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/golang/wen-jian-cao-zuo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
