Golang 文件操作

读取

  1. 一次性读取
go 复制代码
data, err := os.ReadFile("filename.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))
  1. 按行读取

方式1:bufio.NewScanner

go 复制代码
file, err := os.Open("filename.txt")
if err != nil {
	panic(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
	fmt.Println(scanner.Text())
}

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

方式2:bufio.NewReader

go 复制代码
file, err := os.Open("filename.txt")
if err != nil {
	panic(err)
}
defer file.Close()

reader:= bufio.NewReader(file)
for {
	line, _, err := reader.ReadLine()
	if err == io.EOF {
		break
	}
	fmt.Println(string(line))
}

区别是bufio.Scanner 还可自定义扫描的分隔符,如:

go 复制代码
file, _ := os.Open("filename.txt")
scanner := bufio.NewScanner(file)
//scanner.Split(bufio.ScanLines) // 按行读,是默认读取方式
//scanner.Split(bufio.ScanWords) // 按单词读,返回文本中每个以空格分隔的单词,并删除周围的空格
//scanner.Split(bufio.ScanBytes) // 按字节读
//scanner.Split(bufio.ScanRunes) // 按 UTF-8 编码的字符读,如:中文字符

for scanner.Scan() {
	println(scanner.Text())
}
  1. 按指定字节长度读取
    可以使用 bufio.ReaderRead 方法,能读取指定长度的字节到一个切片中
go 复制代码
package main

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

func main() {
	file, err := os.Open("filename.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	buf := make([]byte, 2)  // 指定读取的字节长度

	n, err := reader.Read(buf)
	if err != nil {
		return
	}

	fmt.Printf("读取的字节数: %d\n", n)
	fmt.Printf("读取的内容: %s\n", buf[:n])
}

假如要读取的文件中只有内容hello,那么将输出:

读取的字节数: 2
读取的内容: he

然后在此基础上,使用循环来读整个文件

go 复制代码
package main

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

func main() {
	file, err := os.Open("filename.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	buf := make([]byte, 2) // 指定读取的字节长度

	for {
		n, err := reader.Read(buf)
		if err != nil {
			break
		}
		fmt.Printf("%s\n", buf[:n])
	}
}

其输出将变为:

he
ll
o

写入

  1. 权限表示
    在 Unix 和类 Unix 系统的权限表示中,用3个数字表示,三个数字各代表了不同用户类别的访问权限:
  • 第一个数字:代表文件拥有者(owner)的权限。
  • 第二个数字:代表与文件拥有者同组的用户(group)的权限。
  • 第三个数字:代表其他所有用户(others)的权限。

每个数字可以是0到7之间的任意数字,分别控制读(4)、写(2)和执行(1)权限的开启和关闭。这些数字是对应的权限值的总和

  • 0 没有权限
  • 1 执行权限
  • 2 写权限
  • 4 读权限

一个权限表示是:0666,那么它其实是代表可读可写,前面的 0 是用来明确表示这是一个八进制数

  1. 操作标志
go 复制代码
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
	// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	// The remaining values may be or'ed in to control behavior.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

举几个例子:

go 复制代码
os.O_CREATE|os.O_WRONLY  // 不存在则创建,若存在则从头写入并覆盖原位置的内容,比如原先是hello,然后写入的是demo,那么会变成demoo
os.O_CREATE|os.O_TRUNC|os.O_WRONLY  // 不存在则创建,存在则清空
os.O_APPEND|os.O_WRONLY  // 追加写

常用方法:

go 复制代码
os.OpenFile(name string, flag int, perm FileMode) (*File, error)
os.WriteFile(name string, data []byte, perm FileMode) error  // 存在则清空后以原权限写入,不存在则以指定权限创建
os.Open(name string) (*File, error)  //模式为 O_RDONLY
os.Create(name string) (*File, error)  //Create 创建或清空,模式为 O_RDWR
os.Remove(name string) error  //将指定的文件或目录删除
os.RemoveAll(path string) error  //删除路径及其包含的任何子项

文件复制

go 复制代码
io.Copy(dst Writer, src Reader) (written int64, err error)
go 复制代码
package main

import (
	"io"
	"os"
)

// CopyFile copies a file from src to dst. If src and dst files exist, and are the same, then return error.
// If dst does not exist, it is created with mode 0666 (before umask).
func CopyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		return err
	}

	return out.Sync()
}

func main() {
	src := "source.txt"
	dst := "destination.txt"
	if err := CopyFile(src, dst); err != nil {
		panic(err)
	}
}

操作目录

  1. 创建目录
go 复制代码
package main

import (
	"fmt"
	"os"
)

func main() {
	// 创建一个目录
	err := os.Mkdir("example_dir", 0755)
	if err != nil {
		fmt.Println(err)
	}

	// 创建目录路径中所有不存在的目录
	err = os.MkdirAll("example_dir/subdir", 0755)
	if err != nil {
		fmt.Println(err)
	}
}
  1. 读取目录
go 复制代码
os.ReadDir(name string) ([]DirEntry, error)  // go 1.16 引入

读取指定目录下的文件和文件夹,及其大小

go 复制代码
package main

import (
	"fmt"
	"os"
)

// convertSize 将字节大小转换为更可读的单位
func convertSize(size int64) string {
	const (
		KB = 1 << 10 // 1024
		MB = 1 << 20 // 1024 * 1024
		GB = 1 << 30 // 1024 * 1024 * 1024
	)

	switch {
	case size >= GB:
		return fmt.Sprintf("%.2f GB", float64(size)/GB)
	case size >= MB:
		return fmt.Sprintf("%.2f MB", float64(size)/MB)
	case size >= KB:
		return fmt.Sprintf("%.2f KB", float64(size)/KB)
	default:
		return fmt.Sprintf("%d B", size)
	}
}

func main() {
	entries, err := os.ReadDir(".") // 读取当前目录
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			// 如果无法获取文件信息,则跳过该文件
			fmt.Println("Error:", err)
			continue
		}
		fmt.Printf("%s: %s\n", info.Name(), convertSize(info.Size()))
	}
}

如果想读取子目录中的内容,可以添加一个循环,然后判断是否是目录,然后同样取即可,直至没有子目录

或者使用更高效的filepath.WalkDir(go 1.16),是filepath.Walk(go 1.0)的优化版,性能更好

go 复制代码
package main

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

// convertSize 将字节大小转换为更可读的单位
func convertSize(size int64) string {
	const (
		KB = 1 << 10 // 1024
		MB = 1 << 20 // 1024 * 1024
		GB = 1 << 30 // 1024 * 1024 * 1024
	)

	switch {
	case size >= GB:
		return fmt.Sprintf("%.2f GB", float64(size)/GB)
	case size >= MB:
		return fmt.Sprintf("%.2f MB", float64(size)/MB)
	case size >= KB:
		return fmt.Sprintf("%.2f KB", float64(size)/KB)
	default:
		return fmt.Sprintf("%d B", size)
	}
}

func visitFile(path string, d os.DirEntry, err error) error {
	if err != nil {
		fmt.Printf("访问文件时遇到错误: %v\n", err)
		return nil // 返回nil继续遍历
	}
	if !d.IsDir() {
		info, err := d.Info()
		if err != nil {
			fmt.Printf("获取文件信息时遇到错误: %v\n", err)
			return nil
		}
		fmt.Printf("%s: %s\n", path, convertSize(info.Size()))
	}
	return nil
}

func main() {
	err := filepath.WalkDir(".", visitFile)
	if err != nil {
		fmt.Printf("遍历目录时遇到错误: %v\n", err)
	}
}
相关推荐
@东辰2 小时前
【golang-技巧】-自定义k8s-operator-by kubebuilder
开发语言·golang·kubernetes
@东辰3 小时前
【golang-技巧】- 定时任务 - cron
开发语言·golang·cron
jerry60910 小时前
7天用Go从零实现分布式缓存GeeCache(改进)(未完待续)
分布式·缓存·golang
杜杜的man10 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang
甘橘籽15 小时前
【RPC】 gRPC、pb基本使用--经验与总结
golang
杜杜的man15 小时前
【go从零单排】HTTP客户端和服务端
开发语言·http·golang
材料苦逼不会梦到计算机白富美16 小时前
golang分布式缓存项目 Day6 防止缓存击穿
分布式·缓存·golang
杜杜的man18 小时前
【go从零单排】Environment Variables环境变量
golang
材料苦逼不会梦到计算机白富美20 小时前
golang HTTP基础
http·golang·iphone
友大冰1 天前
Go 语言已立足主流,编程语言排行榜24 年 11 月
开发语言·后端·golang