Skip to content

文件操作

简介

在 Go 中,文件操作主要依赖以下几个标准库:

  • os 包:提供了底层的文件操作接口,比如打开、创建、读取、写入、删除文件等。
  • io 包:提供了基本的 I/O 接口,通常与 os 配合使用。
  • ioutil 包(Go 1.16 之后推荐使用 osio):之前用于简化文件操作,现在功能被分散到其他包中。
  • bufio 包:提供缓冲读写功能,适合处理大文件或需要高效读写的场景。

文件操作的核心是 os.File 类型,它代表一个打开的文件对象,通过它可以执行读取、写入等操作。

创建文件

创建文件可以使用 os 包提供的 Create 函数。

go
func Create(name string) (*File, error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 使用 os 包的 Create 函数创建文件
	file, err := os.Create("example.txt")
	if err != nil {
		fmt.Println("创建文件失败, Error:", err)
		return
	}

	// 输出打印信息
	fmt.Println(file.Name(), "文件成功!")
}

打开文件

常见的两种打开文件的方式是使用 os 包提供的两个函数。

Open 函数以只读方式打开文件。

go
func Open(name string) (*File, error)

OpenFile 函数提供更加灵活的方式打开文件,可以指定打开模式(只读/只写/读写/追加/创建/清空)和权限。

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

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 以只读方式打开文件
	file1, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("打开文件失败, Error:", err)
		return
	}

	// 关闭文件
	defer file1.Close()

	// 输出打印信息
	fmt.Println(file1.Name(), "文件打开成功!")

	// 以读写方式打开文件
	file2, err := os.OpenFile("example.txt", os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("打开文件失败, Error:", err)
		return
	}

	// 关闭文件
	defer file2.Close()

	// 输出打印信息
	fmt.Println(file2.Name(), "文件打开成功!")
}

写入文件

使用 os.File 结构体提供了几个方法,可以实现文件的写入操作。

Write 方法用于将字节切片写入数据到文件中。

go
func (f *File) Write(b []byte) (n int, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开文件,指定文件的打开模式为只写和创建,权限为 0666
	file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}
    
    // 关闭文件
    defer file.Close()

	// os.File 的 Write 方法用于将字节数组写入文件
	n, err := file.Write([]byte("Hello, World!"))
	if err != nil {
		fmt.Println("字节切片内容写入失败, Error:", err)
        return
	}

    // 输出打印信息
    fmt.Println("文件写入成功!写入了", n, "个字节")
}

WriteString 方法用于将字符串写入文件中。

go
func (f *File) WriteString(s string) (n int, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开文件,指定文件的打开模式为只写和创建,权限为 0666
	file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}

    // 关闭文件
	defer file.Close()

    // os.File 的 WriteString 方法用于将字符串写入文件
	n, err := file.WriteString("Hello, World!")
	if err != nil {
		fmt.Println("字符串内容写入失败, Error:", err)
		return
	}

	// 输出打印信息
	fmt.Println("文件写入成功!写入了", n, "个字节")
}

WriteAt 方法用于从指定位置(偏移量 off)写入数据到文件中(不移动文件当前的位置指针)。

go
func (f *File) WriteAt(b []byte, off int64) (n int, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开文件,指定文件的打开模式为只写和创建,权限为 0666
	file, err := os.OpenFile("example.txt", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}

	// 关闭文件
	file.Close()

	// 要写入的数据
	data := []byte("Hello, World!")

	// 在文件的第3个字节位置写入数据
	offset := int64(3)
	n, err := file.WriteAt(data, offset)
	if err != nil {
		fmt.Println("写入文件失败, Error:", err)
		return
	}

    // 输出打印信息
	fmt.Printf("成功写入 %d 个字节\n", n)
}

WriteTo 方法用于将当前文件的内容写到另一个 io.Writer 对象中(例如:另一个文件、网络连接、缓冲区等)。

go
func (f *File) WriteTo(w io.Writer) (n int64, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
    // 打开一个文件
	file1, err := os.Open("file1.txt")
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}

    // 关闭文件
    file1.Close()

    // 创建一个新的文件
	file2, err := os.Create("file2.txt")
	if err != nil {
		fmt.Println("文件创建失败, Error:", err)
		return
	}

    // 将file1.txt的内容写入file2.txt
	n, err := file1.WriteTo(file2)
	if err != nil {
		fmt.Println("文件写入失败, Error:", err)
		return
	}

    // 输出打印信息
	fmt.Println("将file1.txt的内容写入file2.txt成功,写入了", n, "个字节")
}

读取文件

使用 os.Open 或者 os.OpenFile 函数打开文件后,便可以多文件进行读取操作了,*os.File 结构体提供了几个方法用于读取文件。

首先是 Read 方法从文件中读取数据到字节切片中,返回读取的字节数和错误。

go
func (f *File) Read(b []byte) (n int, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}

	// 关闭文件
	defer file.Close()

	// 创建一个长度为 100 的字节切片
	buf := make([]byte, 100)
    // 从文件中读取数据
	n, err := file.Read(buf)
	if err != nil {
		fmt.Println("文件读取失败, Error:", err)
		return
	}

    // 打印成功读取的字节数
	fmt.Println("文件读取成功!读取了", n, "个字节")

    // 打印读取到的内容
	fmt.Println("读取到的内容:", string(buf[:n]))
}

ReadAt 方法从文件的指定偏移量位置读取数据,不改变文件指针位置。

go
func (f *File) ReadAt(b []byte, off int64) (n int, err error)

示例:

go
package main

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

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("文件打开失败, Error:", err)
		return
	}

	// 关闭文件
	defer file.Close()

	// 创建一个字节切片作为读取的缓冲区,缓冲区大小为15
	buf := make([]byte, 15)

	// 从第5个字节开始读取
	n, err := file.ReadAt(buf, 5)
	if err != nil && err != io.EOF {
		fmt.Println("文件读取失败, Error:", err)
		return
	}

	// 打印成功读取的字节数
	fmt.Printf("从偏移量5读取了 %d 个字节.\n", n)

	// 打印读取到的内容
	fmt.Printf("读取到的内容:%s\n", string(buf[:n]))
}

Seek 方法用于将文件指针定位到指定的偏移量位置,返回定位后的偏移量和错误信息。

go
func (f *File) Seek(offset int64, whence int) (int64, error)

示例:

go
package main

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

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("打开文件失败:", err)
		return
	}

	// 关闭文件
	defer file.Close()

	// 将文件指针移动到第5个字节(从开头算起)
	_, err = file.Seek(5, io.SeekStart)
	if err != nil {
		fmt.Println("Seek 失败:", err)
		return
	}

	// 读取5个字节的内容
	buffer := make([]byte, 5)
	n, err := file.Read(buffer)
	if err != nil {
		fmt.Println("读取失败:", err)
		return
	}

    // 打印成功读取的字节数
	fmt.Printf("读取到的内容: %s\n", buffer[:n])
}

获取文件信息

Stat 方法用于获取文件的信息,返回文件信息和错误信息。

go
func Stat(name string) (fi FileInfo, err error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 打开文件
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("打开文件失败:", err)
		return
	}

	// 关闭文件
	defer file.Close()

	// 获取文件信息
	fileInfo, err := file.Stat()
	fmt.Println("文件名:", fileInfo.Name())
    fmt.Println("文件大小:", fileInfo.Size())
    fmt.Println("文件权限:", fileInfo.Mode())
    fmt.Println("文件修改时间:", fileInfo.ModTime())
    fmt.Println("文件是否是目录:", fileInfo.IsDir())
    fmt.Println("文件是否是文件:", fileInfo.Mode().IsRegular())
}

文件重命名

Rename 方法用于将文件从一个路径重命名到另一个路径,返回错误信息。

go
func Rename(oldpath, newpath string) error

示例:

go
package main

import (
    "os"
    "fmt"
)

func main() {
    // 重命名文件
    err := os.Rename("example.txt", "example-new.txt")
    if err != nil {
        fmt.Println("文件重命名失败:", err)
        return
    }

    // 输出打印信息
    fmt.Println("文件重命名成功!")
}

删除文件

Remove 方法用于删除文件,返回错误信息。

go
func Remove(name string) error

示例:

go
package main

import (
    "os"
    "fmt"
)

func main() {
    // 删除文件
    err := os.Remove("example.txt")
    if err != nil {
        fmt.Println("文件删除失败:", err)
        return
    }

    // 输出打印信息
    fmt.Println("文件删除成功!")
}

文件复制

Copy 方法用于复制文件,返回错误信息。

go
func Copy(dstName, srcName string) error

示例:

go
package main

import (
    "os"
    "fmt"
)

func main() {
    // 复制文件
    err := os.Copy("example.txt", "example-copy.txt")
    if err != nil {
        fmt.Println("文件复制失败:", err)
        return
    }

    // 输出打印信息
    fmt.Println("文件复制成功!")
}

文件夹

创建

Mkdir 方法用于创建文件夹,返回错误信息。

go
func Mkdir(name string, perm os.FileMode) error

MkdirAll 方法用于递归创建文件夹,返回错误信息。

go
func MkdirAll(path string, perm os.FileMode) error

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 创建文件夹
	err := os.Mkdir("a", 0755)
	if err != nil {
		fmt.Println("文件夹创建失败:", err)
		return
	}
	fmt.Println("文件夹创建成功!")

	// 创建多层级文件夹
	err = os.MkdirAll("./a/b/c", 0755)
	if err != nil {
		fmt.Println("多层级文件夹创建失败:", err)
		return
	}
	fmt.Println("多层级文件夹创建成功!")
}

读取

ReadDir 方法用于读取文件夹,返回文件夹信息和错误信息。

go
func ReadDir(name string) ([]FileInfo, error)

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 指定要读取文件夹的路径
	dirPath := "./testdir"

	// 读取目录内容
	entries, err := os.ReadDir(dirPath)
	if err != nil {
		fmt.Println("读取目录失败:", err)
		return
	}

	// 遍历目录条目
	fmt.Println("目录内容:")
	for i, entry := range entries {
		// 获取条目名称和类型
		name := entry.Name()
		isDir := entry.IsDir()
		fmt.Printf("%d. %s (%s)\n", i+1, name, map[bool]string{true: "目录", false: "文件"}[isDir])
	}
}

Walk 方法用于递归遍历文件夹,返回错误信息。

go
func Walk(dir string, fn filepath.WalkFunc) error

示例:

go
package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	// 递归读取目录
	// 指定要读取的目录
	dirPath := "./testdir"

	// 递归遍历目录
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err // 如果出错,返回错误
		}

		// 打印路径和类型
		typeStr := "文件"
		if info.IsDir() {
			typeStr = "目录"
		}
		fmt.Printf("%s (%s)\n", path, typeStr)
		return nil
	})

	if err != nil {
		fmt.Println("遍历目录失败:", err)
		return
	}
}

删除

Remove 方法用于单个空目录或文件,返回错误信息。

go
func Remove(name string) error

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 指定要删除的空目录
	dirPath := "emptydir"

	// 创建一个空目录用于测试
	err := os.Mkdir(dirPath, 0755)
	if err != nil {
		fmt.Println("创建目录失败:", err)
		return
	}

	// 删除空目录
	err = os.Remove(dirPath)
	if err != nil {
		fmt.Println("删除目录失败:", err)
		return
	}

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

RemoveAll 方法用于递归删除文件夹及其所有内容(类似 rm -r),返回错误信息。

go
func RemoveAll(path string) error

示例:

go
package main

import (
	"fmt"
	"os"
)

func main() {
	// 指定要删除的目录
	dirPath := "testdir"

	// 创建测试目录和内容
	err := os.MkdirAll(dirPath+"/subdir", 0755)
	if err != nil {
		fmt.Println("创建目录失败:", err)
		return
	}
	err = os.WriteFile(dirPath+"/file1.txt", []byte("Hello"), 0644)
	if err != nil {
		fmt.Println("创建文件失败:", err)
		return
	}

	// 递归删除目录及其内容
	err = os.RemoveAll(dirPath)
	if err != nil {
		fmt.Println("删除目录失败:", err)
		return
	}

	fmt.Println("目录及其内容删除成功!")
}

复制

在 Go 语言中,标准库没有直接提供“文件夹复制”的函数,可以编写一个函数递归遍历整个文件夹,然后递归复制每个文件和子文件夹。

一个简单的文件夹复制的代码示例如下。

go
package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
)

func copyDir(src, dst string) error {
	// 确保源路径存在且是目录
	srcInfo, err := os.Stat(src)
	if err != nil {
		return fmt.Errorf("源路径错误: %v", err)
	}
	if !srcInfo.IsDir() {
		return fmt.Errorf("源路径不是目录: %s", src)
	}

	// 创建目标目录
	err = os.MkdirAll(dst, srcInfo.Mode())
	if err != nil {
		return fmt.Errorf("创建目标目录失败: %v", err)
	}

	// 遍历源目录
	err = filepath.Walk(src, func(srcPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 计算目标路径
		relPath, err := filepath.Rel(src, srcPath)
		if err != nil {
			return err
		}
		dstPath := filepath.Join(dst, relPath)

		// 如果是目录,创建目录
		if info.IsDir() {
			return os.MkdirAll(dstPath, info.Mode())
		}

		// 如果是文件,复制文件内容
		return copyFile(srcPath, dstPath)
	})

	return err
}

func copyFile(src, dst string) error {
	// 打开源文件
	srcFile, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("打开源文件失败: %v", err)
	}
	defer srcFile.Close()

	// 创建目标文件
	dstFile, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("创建目标文件失败: %v", err)
	}
	defer dstFile.Close()

	// 复制文件内容
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return fmt.Errorf("复制文件内容失败: %v", err)
	}

	// 同步文件权限
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	return os.Chmod(dst, srcInfo.Mode())
}

func main() {
	// 定义源目录和目标目录
	srcDir := "source"
	dstDir := "destination"

	// 创建测试目录和文件
	err := os.MkdirAll(srcDir+"/subdir", 0755)
	if err != nil {
		fmt.Println("创建源目录失败:", err)
		return
	}
	err = os.WriteFile(srcDir+"/file1.txt", []byte("Hello, World!"), 0644)
	if err != nil {
		fmt.Println("创建源文件失败:", err)
		return
	}

	// 执行复制
	err = copyDir(srcDir, dstDir)
	if err != nil {
		fmt.Println("复制文件夹失败:", err)
		return
	}

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

参考资料

根据 MIT 许可证发布