Go 文件操作


文件操作

创建文件

Go 语言中提供了一个 Create() 函数用于创建文件。

该函数在创建文件时,首先会判断要创建的文件是否存在,如果不存在,则创建,如果存在,会先将文件中已有的数据清空。

同时,当文件创建成功后,该文件会默认的打开,所以不用再执行打开操作,可以直接向该文件中写入数据。

创建文件的步骤:

  1. 导入 os 包,创建文件(读写文件的函数都在该包)
  2. 指定创建的文件存放路径以及文件名
  3. 执行 Create() 函数,进行文件创建
  4. 关闭文件
import (
    "fmt"
    "os"
)

func CreateFile(path string) {
    //创建文件,返回两个值,一是创建的文件,二是错误信息
    file, err := os.Create(path)
    if err != nil {
        fmt.Println("err=", err)  // 有错误时则打印
        return
    }
    defer file.Close() // 在退出函数时,关闭文件
}

func main() {
    var filePath = "a.txt"
    CreateFile(filePath)
}

写入数据

WriteString()

import (
    "fmt"
    "os"
)

func CreateFile(path string) {
    //创建文件,返回两个值,一是创建的文件,二是错误信息
    file, err := os.Create(path)
    if err != nil {
        fmt.Println("err=", err) // 有错误时则打印
        return
    }
    for i := 0; i < 10; i++ {
        // n表示写入的字符长度,err表示错误信息
        n, err := file.WriteString("hello go\n") // 该方法默认不换行
        if err != nil {
            fmt.Println("err=", err) // 有错误时则打印
            return
        }
        fmt.Println("write len=", n) // 9
    }
    defer file.Close() // 在退出函数时,关闭文件
}

func main() {
    var filePath = "a.txt"
    CreateFile(filePath)
}

Write()

使用 Write() 函数写数据时,参数为字节切片,因此所以需要将字符串转换成字节切片。该方法返回的也是写入文件数据的长度。

import (
    "fmt"
    "os"
)

func CreateFile(path string) {
    //创建文件,返回两个值,一是创建的文件,二是错误信息
    file, err := os.Create(path)
    if err != nil {
        fmt.Println("err=", err) // 有错误时则打印
        return
    }
    for i := 0; i < 10; i++ {
        // n表示写入的字符长度,err表示错误信息
        str := fmt.Sprintf("i=%d\n", i)  // 格式化字符串
        buf := []byte(str)
        n, err := file.Write(buf) // 该方法默认不换行
        if err != nil {
            fmt.Println("err=", err) // 有错误时则打印
            return
        }
        fmt.Println("write len=", n) // 4
    }
    defer file.Close() // 在退出函数时,关闭文件
}

WriteAt()

该函数作用是在指定位置写入数据。

import (
    "fmt"
    "os"
)

func CreateFile(path string) {
    //创建文件,返回两个值,一是创建的文件,二是错误信息
    file, err := os.Create(path)
    if err != nil {
        fmt.Println("err=", err) // 有错误时则打印
        return
    }
    for i := 0; i < 10; i++ {
        // n表示写入的字符长度,err表示错误信息
        str := fmt.Sprintf("i=%d\n", i)
        buf := []byte(str)
        // 找到文件末尾(偏移量)
        offset, _ := file.Seek(0, os.SEEK_END)
        // 从文件末尾(偏移量)写入内容
        n, err := file.WriteAt(buf, offset)
        if err != nil {
            fmt.Println("err=", err) // 有错误时则打印
            return
        }
        fmt.Println("write len", n) // 4
    }
    defer file.Close() // 在退出函数时,关闭文件
}

func main() {
    var filePath = "a.txt"
    CreateFile(filePath)
}

以上程序中 Seek() 函数返回值存储到变量 n 中,值为文件末尾的位置,且 WriteAt() 返回的也是写入的数据长度。


追加数据

对已经存在的文件不能再执行 Create(),而是要执行 OpenFile() 。

import (
    "fmt"
    "os"
)

func WriteFile(path string) {
    //打开文件(文件路径, 模式, 权限)
    file, err := os.OpenFile(path, os.O_APPEND, 6)
    if err != nil {
        fmt.Println("err=", err) // 有错误时则打印
        return
    }
    n, err := file.WriteString("append hello go\n")
    if err != nil {
        fmt.Println("err=", err)
        return
    }
    fmt.Println("write len", n)
    defer file.Close()
}

func main() {
    var filePath = "a.txt"
    WriteFile(filePath)
}

常见的模式(参数二)有:

  • O_RDONLY:只读模式
  • O_WRONLY:只写模式
  • O_RDWR:可读可写模式
  • O_APPEND:追加模式

权限(参数三)表示如下:

  • 0:没有任何权限
  • 1:执行权限(如果是可执行文件,是可以运行的)
  • 2:写权限
  • 3:写、执行权限
  • 4:读权限
  • 5:读、执行权限
  • 6:读、写权限
  • 7:读、写、执行权限

读取文件

如果文件已经存在,并且也已经有数据了,那么可以直接读取该文件中的内容。

读取文件的基本流程如下:

  1. 打开要读取的文件
  2. 对文件进行读取
  3. 关闭文件

Read()

关于 Read() 函数的使用如下:

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

func ReadFile(path string) {
    file, err := os.Open(path)
    if err != nil {
        fmt.Println("err=", err)
        return
    }
    buf := make([]byte, 1024) // 1k的大小
    // n 表示从文件中读取的字符长度
    n, err := file.Read(buf)
    if err != nil && err != io.EOF { // 文件出错,且同时没有到结尾
        fmt.Println("err=", err)
        return
    }
    fmt.Println("buf=", string(buf[:n]))
    defer file.Close()
}

func main() {
    var filePath = "a.txt"
    ReadFile(filePath)
}

行读取

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

func ReadFile(path string) {
    file, err := os.Open(path)
    if err != nil {
        fmt.Println("err=", err)
        return
    }
    // 把内容先放缓冲区
    r := bufio.NewReader(file)
    // 循环读取文件内容,直至末尾
    for {
        // 遇到\n时读取并结束
        buf, err := r.ReadBytes('\n')
        if err != nil {
            if err == io.EOF {
                break
            }
            fmt.Println("err=", err)
        }
        // ReadBytes() 返回的是字节切片,所以在打印时要转换成字符串
        fmt.Printf("buf=%s", buf)
    }
    defer file.Close()
}

func main() {
    var filePath = "a.txt"
    ReadFile(filePath)
}

案例:复制文件内容

需求:文件拷贝,将已有的文件复制一份,同时重新命名

实现思路:

  1. 让用户输入要拷贝的文件的名称(源文件)以及目的文件名称
  2. 创建目的文件
  3. 打开源文件,并且读取该文件中的内容
  4. 将从源文件中读取的内容写到目的文件中

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

func CopyFile() {
    var srcFile string
    var dstFile string
    fmt.Println("请输入源文件:")
    fmt.Scan(&srcFile)
    fmt.Println("请输入目的文件:")
    fmt.Scan(&dstFile)
    if srcFile == dstFile {
        fmt.Println("源文件和目的文件不能相同")
        return
    }
    // 只读方式打开源文件
    openFile, err1 := os.Open(srcFile)
    if err1 != nil {
        fmt.Println("open err: ", err1)
        return
    }
    // 创建目的文件
    writeFile, err2 := os.Create(dstFile)
    if err2 != nil {
        fmt.Println("write err: ", err2)
        return
    }
    // 从源文件读取内容
    // 临时缓冲区
    buf := make([]byte, 1024)
    for {
        n, err3 := openFile.Read(buf) // 每次读取1k内容
        if err3 != nil {
            // 文件读取完毕
            if err3 == io.EOF {
                fmt.Println("copy done")
                break
            }
            fmt.Println("copy err: ", err3)
        }
        // 往目的文件写,赌多少写多少
        writeFile.Write(buf[:n])
    }
    defer openFile.Close()
    defer writeFile.Close()
}

func main() {
    CopyFile()
}

目录操作

我们读写的文件一般存放于目录中。因此,有时需要指定到某一个目录下,根据目录存储的状况再进行文件的特定操作。

其实,目录也可以看成“文件”。通常我们读写的文件内容是可见的 ASCII 码,而目录文件的内容就是文件名和目录名,称之为目录项。我们读写目录文件,实质上就是在读写目录项。


打开目录

打开目录和打开文件一样,也是使用 OpenFile() 函数,但要指定不同的参数来通知系统,表示要打开的是目录文件。

func OpenFile(name string, flag int, perm FileMode) (*File, error) 
  • name:表示要打开的目录名称。使用绝对路径较多
  • flag:表示打开文件的读写模式。可选择:
    • O_RDONLY:只读模式
    • O_WRONLY:只写模式
    • O_RDWR:读写模式
  • perm:表示打开权限(对于目录来说略有不同,通常传 os.ModeDir)
  • 返回值:由于是操作目录,所以 file 是指向目录的文件指针,error 中保存错误信息

读目录内容

目录中存放的是文件名和子目录名,使用 Readdir() 函数来完成。

func (f *File) Readdir(n int) ([]FileInfo, error)
  • n:表示读取目录的成员个数。通常传 -1,表示读取目录所有文件对象
  • 返回值:FileInfo 类型的切片(其内部保存了文件名);error 保存错误信息
type FileInfo interface {
   Name() string       // base name of the file
   Size() int64        // length in bytes for regular files; system-dependent for others
   Mode() FileMode     // file mode bits
   ModTime() time.Time // modification time
   IsDir() bool        // abbreviation for Mode().IsDir()
   Sys() interface{}   // underlying data source (can return nil)
}

得到 FileInfo 类型切片后,可以 range 遍历切片元素,使用 .Name() 获取文件名,使用 .Size() 获取文件大小,使用 .IsDir() 判断文件是目录还是非目录文件。

示例:提示用户提供一个目录位置后,打开该目录,查看目录下的所有成员,并判断是文件还是目录

func main()  {
    fmt.Println("请输入要找寻的目录:")
    var path string
    fmt.Scan(&path)  // 获取用户指定的目录名
    // 只读打开该目录
    dir, _ := os.OpenFile(path, os.O_RDONLY, os.ModeDir)   
    // 读取当前目录下所有的文件名和目录名,存入names切片
    names, _ := dir.Readdir(-1)
    // 遍历切片,获取文件/目录名
    for _, name := range names {
       if !name.IsDir() {
          fmt.Println(name.Name(), "是一个文件")
       } else {
          fmt.Println(name.Name(), "是一个目录")
       }
    }
}

其他 API

目录操作还有其他的一系列 API,这里简单罗列几个较为常用的:

// 将当前工作目录修改为 dir 指定的目录
func Chdir(dir string) error
// 返回当前工作目录的绝对路径
func Getwd() (dir string, err error)
// 使用指定的权限和名称创建一个目录
func Mkdir(name string, perm FileMode) error

获取更多文件、目录操作 API,可查看 Go 标库文档

posted @ 2023-03-14 00:19  Juno3550  阅读(91)  评论(0编辑  收藏  举报