Golang IO 编程
1. JSON 序列化
序列化就是把比变量转为二进制流 ( 二进制流可以跟[]byte
等价,[]byte
又可以跟 string 等价 )
序列化的目的是为了写入磁盘或者发送到网络上
type User struct {
ID int // 默认的JSON字段名和原始变量名一致
Name string `json:"name"` // 自定义JSON字段名
age int // 不可导出的成员变量不会被序列化(不会被导出到磁盘或者网络)
Birthday time.Time // 2023-11-25 23:10:52.3471691 +0800 CST m=+0.003135501
CreatedAt MyDate // 自定义结构体
}
标准库使用反射进行 JSON 序列化,通过反射可以在运行时动态的获得结构器成员变量的名称,( JSON ),是否可以导出,可以获取成员变量,还可以调用结构体的方法(比如MarshalJSON和 UnmarhshalJSON)
2. 并行处理文件
首先统计路径下的所有文件和目录
// 统计一个路径下有个几个文件或者目录
func CountDirAndFile(dir string) error {
Files := make([]string, 0, 10)
Dirs := make([]string, 0, 10)
// WalkDir内部会递归遍历目录
// fs.DirEntry 代表一个目录项,包含了文件名、文件类型、文件权限等信息
filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
log.Fatal(err)
}
// 通过fs.DirEntry.Info()获取文件信息
info, err := d.Info()
if err != nil {
log.Fatal(err)
}
// IsRegular()判断是否是文件
// IsDir()判断是否是目录
if info.Mode().IsRegular() {
Files = append(Files, path)
} else if info.Mode().IsDir() {
// 因为路径也会被当成目录,所以不应该在Dir中增加传入的路径
if path == dir {
} else {
Dirs = append(Dirs, path)
}
}
return nil
})
fmt.Println("路径", dir, "下面有", len(Files), "个文件, ", len(Dirs), "个目录")
fmt.Println(Files)
fmt.Println(Dirs)
return Files
}
3. 并行写入文件
打开文件后通过 goroutine 并发的写入内容
var Content = "Sakura 2023-11-26\n"
// 并发写入文件
// os.File支持并发调用
func FileWrite(goroutine int, file *os.File) {
wg := sync.WaitGroup{}
wg.Add(goroutine)
for i := 0; i < goroutine; i++ {
go func() {
defer wg.Done()
for j := 0; j < 10000; j++ {
if _, err := file.WriteString(Content); err != nil {
panic(err)
}
}
}()
}
wg.Wait()
}
测试
func TestFileRead(t *testing.T) {
path := "data/2.txt"
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
fmt.Println(err)
}
for i := 0; i < 100000; i++ {
file.WriteString("Sakura 2023 \n")
}
}
串行写文件
func TestFileRead(t *testing.T) {
path := "data/2.txt"
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
fmt.Println(err)
}
for i := 0; i < 100000; i++ {
file.WriteString("Sakura 2023-11-26\n")
}
}
可以看到并发写文件是没有串行写文件快的
并发写文件的使用场景:一个并发执行的接口下调用写日志
5. 并行读文件
6. 压缩和解压
Golang 标准库 compress
包下实现了 bzip2 , DEFLATE , gzip , lzw 和 zlib 压缩算法
压缩
// 使用Gzip压缩
func GzipCompress(InFile, OutFile string) {
// 打开要压缩的文件
SourceFile, err := os.Open(InFile)
if err != nil {
log.Fatal(err)
}
defer SourceFile.Close()
// 统计文件信息
fileInfo, _ := SourceFile.Stat()
fmt.Println("压缩前的文件大小为:", fileInfo.Size())
// 创建压缩后的文件
DestFile, err := os.OpenFile(OutFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
log.Fatal(err)
}
defer DestFile.Close()
// 1.创建gzipwriter
gzipWriter := gzip.NewWriter(DestFile)
defer gzipWriter.Close()
// 把一个流复制到另一个流中
io.Copy(gzipWriter, SourceFile)
//
//gzipWriter.Close()
//DestFile.Close()
//SourceFile.Close()
}
单元测试
func TestGzipCompress(t *testing.T) {
// 定义要压缩的文件和解压后的文件
SourceFile := "Sakura.txt"
DestFile := "Sakura.txt.gzip"
// 调用压缩函数
GzipCompress(SourceFile, DestFile)
//// 调用解压函数
//GzipUnCompress(DestFile, "Sakura.txt")
}
解压
// 解压
func GzipDeCompress(InFile, OutFile string) {
// 打开解压的文件
SourceFile, err := os.Open(InFile)
if err != nil {
log.Fatal(err)
}
defer SourceFile.Close()
stat, err := SourceFile.Stat()
fmt.Println(stat.Name(), "的大小为:", stat.Size(), "B")
// 创建解压后的文件
DestFile, err := os.OpenFile(OutFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
log.Fatal(err)
}
defer DestFile.Close()
// 2.创建gzipreader
gzipReader, err := gzip.NewReader(SourceFile)
if err != nil {
log.Fatal(gin.Default())
}
defer gzipReader.Close()
io.Copy(DestFile, gzipReader)
}
单元测试
func TestGzipCompress(t *testing.T) {
// 定义要压缩的文件和解压后的文件
SourceFile := "Sakura.txt"
DestFile := "Sakura.txt.gzip"
// 调用压缩函数
GzipCompress(SourceFile, DestFile)
// 调用解压函数
GzipDeCompress(DestFile, "Sakura_DeCompress.txt")
}
压缩的文件和解压后的文件
评论区