零基础 go - 58(文件操作)

1 阅读9分钟

一、文件的概念

  • 文件是数据源的一种,比如日常使用的 word文件 txt文件 excel文件。。。

  • 文件最主要的作用就是保存数据,可以保存图片、音频、视频等二进制数据,也可以保存结构化的文本数据,比如 json csv xml等

二、输入流和输出流

  • 文件在程序中是以流的形式来操作的

  • 输入流:从文件中读取数据的流(读文件)

  • 输出流:向文件中写入数据的流(写文件)

三、文件的打开和关闭

  • 使用 file, err := os.Open(文件路径) 打开文件, 会返回一个文件对象和一个错误对象

image.png

  • 使用 defer file.Close() 来确保文件在使用完毕后被正确关闭,释放资源

  • file 是 *File 类型的对象,包含了文件的相关信息和方法


package main

import(

"fmt"

"os"

)

func main() {

    // 打开文件

    file, err := os.Open("/Users/zyq/go/1.txt")

    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }

    // 关闭文件

    defer file.Close()

    fmt.Println("文件打开成功", file, file.Name()) // file 是 *File 类型的对象,包含了文件的相关信息和方法

}

四、文件的读写

1、读取文件

image.png

  • os.ReadFile(文件路径) 方法:直接读取文件的全部内容,返回一个字节切片和一个错误对象

  • os.ReadAll(file) 方法: 一次性读取文件的全部内容,返回一个字节切片和一个错误对象

  • 逐行读取文件的内容,使用 bufio.NewScanner(file) 创建一个扫描器对象,然后使用 scanner.Scan() 方法逐行读取文件内容,使用 scanner.Text() 方法获取

  • 带缓存区的读取文件内容,使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,然后使用 reader.ReadString('\r\n') 方法逐行读取文件内容,直到遇到换行符

  • 使用 io.EOF 来判断文件是否读取到末尾


package main

import(

    "fmt"

    "os"

    "io/ioutil"

    "bufio"

)

func main() {

    // 打开文件

    file, err := os.Open("/Users/zyq/go/1.txt")


    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }
    // 关闭文件

    defer file.Close()

  


    // 方法一:直接读取文件的全部内容

    content, err := os.ReadFile("/Users/zyq/go/1.txt")

    if err != nil {

        fmt.Println("读取文件失败", err)

        return

    }

    fmt.Println("文件内容:", string(content))




    // 方法二:使用 os.ReadAll(file) 方法一次性读取文件的全部内容

    content1, err := io.ReadAll(file)

    if err != nil {

        fmt.Println("读取文件失败", err)

        return

    }

    fmt.Println("文件内容:", string(content1))

  


    // 方法三:逐行读取文件的内容,使用 bufio.NewScanner(file) 创建一个扫描器对象,然后使用 scanner.Scan() 方法逐行读取文件内容,使用 scanner.Text() 方法获取

    scanner := bufio.NewScanner(file)

    for scanner.Scan() {

        line := scanner.Text()

        fmt.Println("文件内容:", line)

    }

    if err := scanner.Err(); err != nil {

        fmt.Println("读取文件失败", err)

        return

    }

  


    // 方法四:带缓存区的读取文件内容,使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,然后使用 reader.ReadString('\n') 方法逐行读取文件内容,直到遇到换行符

    reader := bufio.NewReader(file)

    for {

        line, err := reader.ReadString('\n') // 以换行符为分隔符读取文件内容

        if err != nil {

            if err == io.EOF {

                fmt.Println("文件读取完毕")

                break

            }

            fmt.Println("读取文件失败", err)

            return

        }

        fmt.Println("文件内容:", line)

    }

}

2、写入文件

  • file, err := os.OpenFile(文件路径, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 方法: 打开一个文件用于写入,如果文件不存在则创建,如果文件存在则清空内容,返回一个文件对象和一个错误对象;然后使用 file.WriteString(内容) 方法向文件中写入字符串,返回写入的字节数和一个错误对象

  • 使用 os.WriteFile(文件路径, []byte(内容), 0644) 方法直接将内容写入文件,如果文件不存在则创建,如果文件存在则覆盖内容,返回一个错误对象

  • 使用 writer := bufio.NewWriter(file) 创建一个带缓存区的写入器对象,然后使用 writer.WriteString(内容) 方法向文件中写入字符串,最后使用 writer.Flush() 方法将缓存区中的内容写入文件


package main

import(

    "fmt"

    "os"

    "io"

    "bufio"

)

func main() {

    // 1、打开文件用于写入,如果文件不存在则创建,如果文件存在则清空内容

    // os.OpenFile + os.O_WRONLY + os.O_CREATE + os.O_TRUNC

    // file.WriteString

    file, err := os.OpenFile("/Users/zyq/go/1.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)

    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }

    // 关闭文件

    defer file.Close()

    // 写入 5 句“hello”

    for i := 0; i < 5; i++ {

        _, err := file.WriteString("hello\n")

        if err != nil {

            fmt.Println("写入文件失败", err)

            return

        }

    }




    // 2、打开文件用于写入,如果文件不存在则创建,如果文件存在则追加内容

    // os.OpenFile + os.O_WRONLY + os.O_CREATE + os.O_APPEND

    // bufio.NewWriter + writer.WriteString + writer.Flush

    file, err = os.OpenFile("/Users/zyq/go/1.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)

    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }

    // 关闭文件

    defer file.Close()

    // 追加写入 5 句“world”

    writer := bufio.NewWriter(file)

    for i := 0; i < 5; i++ {

        _, err := writer.WriteString("world\n")

        if err != nil {

            fmt.Println("写入文件失败", err)

            return

        }

    }

    // 将缓存区中的内容写入文件

    err = writer.Flush()

    if err != nil {

        fmt.Println("刷新文件失败", err)

        return

    }




    // 3、打开文件用于读写,将原来的内容显示出来,然后再写入新的内容

    // os.OpenFile + os.O_RDWR + os.O_APPEND

    // bufio.NewReader + reader.ReadString + reader.Flush

    file, err = os.OpenFile("/Users/zyq/go/1.txt", os.O_RDWR|os.O_APPEND, 0644)

    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }

    // 关闭文件

    defer file.Close()

    // 读取文件内容

    reader := bufio.NewReader(file)

    for {

        line, err := reader.ReadString('\n')

        if err != nil {

            if err == io.EOF {

                fmt.Println("文件读取完毕")

                break

            }

            fmt.Println("读取文件失败", err)

            return

        }

        fmt.Println("文件内容:", line)

    }

    // 写入新的内容

    _, err = file.WriteString("hello world\n")

    if err != nil {

        fmt.Println("写入文件失败", err)

        return

    }




    // 4、使用 os.WriteFile(文件路径, []byte(内容), 0644) 方法直接将内容写入文件,如果文件不存在则创建,如果文件存在则覆盖内容

    content1 := "hello world\n"

    err = os.WriteFile("/Users/zyq/go/1.txt", []byte(content1), 0644)

    if err != nil {

        fmt.Println("写入文件失败", err)

        return

    }

}

五、判断文件或目录是否存在

  • 使用 os.Stat(文件路径) 方法获取文件或目录的信息,如果返回的错误对象不为 nil 且错误类型为 os.IsNotExist(err),则说明文件或目录不存在

package main

import(

    "fmt"

    "os"

)

func main() {

    // 判断文件是否存在

    _, err := os.Stat("/Users/zyq/go/1.txt")

    if os.IsNotExist(err) {

        fmt.Println("文件不存在")

    } else {

        fmt.Println("文件存在")

    }

    // 判断目录是否存在

    _, err = os.Stat("/Users/zyq/go")

    if os.IsNotExist(err) {

        fmt.Println("目录不存在")

    } else {

        fmt.Println("目录存在")

    }

}

六、文件的拷贝

  • 使用 io.Copy(dst, src) 方法将 src 文件的内容复制到 dst 文件中,dst 和 src 都是 *File 类型的对象

package main

import(

    "fmt"

    "os"

    "io"

)

func main() {

    // 打开源文件

    srcFile, err := os.Open("/Users/zyq/go/1.txt")

    // 处理错误

    if err != nil {

        fmt.Println("打开源文件失败", err)

        return

    }

    // 关闭源文件

    defer srcFile.Close()

    // 创建目标文件

    dstFile, err := os.Create("/Users/zyq/go/2.txt")

    // 处理错误

    if err != nil {

        fmt.Println("创建目标文件失败", err)

        return

    }

    // 关闭目标文件

    defer dstFile.Close()

    // 复制文件内容

    _, err = io.Copy(dstFile, srcFile)

    if err != nil {

        fmt.Println("复制文件失败", err)

        return

    }

    fmt.Println("文件复制成功")

}

七、文件的移动和重命名

  • 使用 os.Rename(oldPath, newPath) 方法将 oldPath 路径的文件或目录移动到 newPath 路径,如果 newPath 已经存在则会被覆盖

  • 移动文件和重命名文件本质上是一样的操作,都是使用 os.Rename() 方法来实现的,只不过移动文件是改变了文件的路径,而重命名文件是改变了文件的名称


package main

import(

    "fmt"

    "os"

)

func main() {

    // 移动文件

    err := os.Rename("/Users/zyq/go/1.txt", "/Users/zyq/go/2.txt")

    if err != nil {

        fmt.Println("移动文件失败", err)

        return

    }

    fmt.Println("文件移动成功")

    // 重命名文件

    err = os.Rename("/Users/zyq/go/2.txt", "/Users/zyq/go/3.txt")

    if err != nil {

        fmt.Println("重命名文件失败", err)

        return

    }

    fmt.Println("文件重命名成功")

}

八、文件的删除

  • 使用 os.Remove(文件路径) 方法删除指定路径的文件或目录,如果路径是一个目录,则该目录必须为空才能被删除

package main

import(

    "fmt"

    "os"

)

func main() {

    // 删除文件

    err := os.Remove("/Users/zyq/go/1.txt")

    if err != nil {

        fmt.Println("删除文件失败", err)

        return

    }

    fmt.Println("文件删除成功")

    // 删除目录

    err = os.Remove("/Users/zyq/go/test")

    if err != nil {

        fmt.Println("删除目录失败", err)

        return

    }

    fmt.Println("目录删除成功")

}

九、文件的权限和模式

  • 文件的权限和模式是用一个三位八进制数来表示的,每一位八进制数代表了文件所有者、文件所属组和其他用户的权限

  • 每一位八进制数的值是由读权限(4)、写权限(2)和执行权限(1)相加得到的,比如 7 代表读、写和执行权限,6 代表读和写权限,5 代表读和执行权限,4 代表读权限,3 代表写和执行权限,2 代表写权限,1 代表执行权限,0 代表没有权限

  • 使用 os.Chmod(文件路径, 权限) 方法来改变文件的权限,其中权限是一个 os.FileMode 类型的值,可以使用 os.FileMode(权限) 来将一个八进制数转换为 os.FileMode 类型的值


package main

import(

    "fmt"

    "os"

)

func main() {

    // 改变文件权限为 755,即文件所有者具有读、写和执行权限,文件所属组和其他用户具有读和执行权限

    err := os.Chmod("/Users/zyq/go/1.txt", os.FileMode(0755))

    if err != nil {

        fmt.Println("改变文件权限失败", err)

        return

    }

    fmt.Println("文件权限改变成功")

}

十、文件的属性和信息

  • 使用 os.Stat(文件路径) 方法获取文件或目录的信息,返回一个 os.FileInfo 类型的对象和一个错误对象,os.FileInfo 对象包含了文件的名称、大小、权限、修改时间等信息

package main

import(

    "fmt"

    "os"

)

func main() {

    // 获取文件信息

    fileInfo, err := os.Stat("/Users/zyq/go/1.txt")

    if err != nil {

        fmt.Println("获取文件信息失败", err)

        return

    }

    fmt.Println("文件名称:", fileInfo.Name())

    fmt.Println("文件大小:", fileInfo.Size())

    fmt.Println("文件权限:", fileInfo.Mode())

    fmt.Println("文件修改时间:", fileInfo.ModTime())

}

十一、统计文件中的英文、数字、空格和其他字符数量

  • 通过读取文件的内容,然后遍历每个字符,使用 unicode 包中的函数来判断字符的类型,分别统计英文、数字、空格和其他字符的数量

package main

import(

    "fmt"

    "os"

    "io"

    "unicode"

)

func main() {

    // 打开文件

    file, err := os.Open("/Users/zyq/go/1.txt")

    // 处理错误

    if err != nil {

        fmt.Println("打开文件失败", err)

        return

    }

    // 关闭文件

    defer file.Close()

  
  
  
  


    // 统计字符数量: 使用 bufio.NewReader(file) 创建一个带缓存区的读取器对象,

    // 然后使用 reader.ReadRune() 方法逐个读取文件中的字符,直到遇到文件末尾,

    // 使用 unicode.IsLetter()、unicode.IsDigit()、unicode.IsSpace() 来判断字符的类型,

    // 并分别统计英文、数字、空格和其他字符的数量

    var letterCount1, digitCount1, spaceCount1, otherCount1 int

    reader := bufio.NewReader(file)

    for {

        char, _, err := reader.ReadRune()

        if err != nil {

            if err == io.EOF {

                fmt.Println("文件读取完毕")

                break

            }

            fmt.Println("读取文件失败", err)

            return

        }

        if unicode.IsLetter(char) {

            letterCount1++

        } else if unicode.IsDigit(char) {

            digitCount1++

        } else if unicode.IsSpace(char) {

            spaceCount1++

        } else {

            otherCount1++

        }

    }

    fmt.Printf("英文字符数量:%d\n数字字符数量:%d\n空格字符数量:%d\n其他字符数量:%d\n", letterCount1, digitCount1, spaceCount1, otherCount1)





    // 另一种方法:使用 reader.ReadString('\n') 方法逐行读取文件内容,然后遍历每行的每个字符来统计数量

    var letterCount, digitCount, spaceCount, otherCount int

    for {

        c, err := reader.ReadString('\n')

        if err != nil {

            if err == io.EOF {

                fmt.Println("文件读取完毕")

                break

            }

            fmt.Println("读取文件失败", err)

            return

        }

    str := []rune(c)

    for _, char := range str {

        switch {

            case char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z':

                letterCount++

            case char >= '0' && char <= '9':

                digitCount++

            case char == ' ' || char == '\t' || char == '\n':

                spaceCount++
    
            default:

                otherCount++

            }

        }

    }

}

十二、文件的路径和目录操作

  • 使用 path/filepath 包来处理文件路径和目录操作,常用的方法有 filepath.Join() 来连接路径,filepath.Dir() 来获取目录,filepath.Base() 来获取文件名,filepath.Ext() 来获取文件扩展名,filepath.Walk() 来遍历目录等

package main

import(

    "fmt"

    "path/filepath"

)

func main() {

    // 连接路径

    path := filepath.Join("/Users/zyq/go", "1.txt")

    fmt.Println("连接后的路径:", path) // 输出:/Users/zyq/go/1.txt

    // 获取目录

    dir := filepath.Dir("/Users/zyq/go/1.txt")

    fmt.Println("文件所在目录:", dir) // 输出:/Users/zyq/go

    // 获取文件名

    fileName := filepath.Base("/Users/zyq/go/1.txt")

    fmt.Println("文件名:", fileName) // 输出:1.txt

    // 获取文件扩展名

    ext := filepath.Ext("/Users/zyq/go/1.txt")

    fmt.Println("文件扩展名:", ext) // 输出:.txt

    // 遍历目录

    err := filepath.Walk("/Users/zyq/go", func(path string, info os.FileInfo, err error) error {

        if err != nil {

            fmt.Println("访问路径失败", err)

            return err

        }

        fmt.Println("访问路径:", path)

        return nil

    })

    if err != nil {

        fmt.Println("遍历目录失败", err)

        return

    }

}