Golang檔案操作-下篇

語言: CN / TW / HK

1、檔案的重新命名和刪除

os 包自帶重新命名和刪除的方法

package main

import "os"

func main()  {
	os.Rename("user.log", "user.v2.log")
	os.Remove("user.txt")
}

2、檔案路徑的獲取

檔案路徑操作包括對檔案路徑、檔名等

package main

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

func main() {
	fmt.Println(filepath.Abs("."))  // 獲取當前路徑,和Getwd相同
	fmt.Println(os.Args)
	fmt.Println(filepath.Abs(os.Args[0]))  // 當前程式執行的絕對路徑
	path, _ := filepath.Abs(os.Args[0])

	fmt.Println(filepath.Base("c:/test/a.txt"))  // basename 檔名 a.txt
	fmt.Println(filepath.Base("c:/test/xxxx/"))  // basename 目錄名 xxxx
	fmt.Println(filepath.Base(path))

	fmt.Println(filepath.Dir("c:/test/a.txt"))  // 獲取目錄名 c:/test
	fmt.Println(filepath.Dir("c:/test/xxxx/"))  // 獲取目錄名 c:/test/xxxx
	fmt.Println(filepath.Dir(path))

	fmt.Println(filepath.Ext("c:/test/a.txt"))  // 獲取副檔名 .txt
	fmt.Println(filepath.Ext("c:/test/xxxx/a"))  // 空
	fmt.Println(filepath.Ext(path))  					// 空

	dirPath := filepath.Dir(path)
	iniPath := dirPath + "/conf/ip.ini"
	fmt.Println(iniPath)  // 拼接配置檔案路徑

	fmt.Println(filepath.Join(dirPath, "conf", "ip.ini"))  // 組裝配置檔案路徑
	fmt.Println(filepath.Glob("./[ab]*/*.go"))   // 找檔案 找當前路徑下目錄名包含ab,以go檔案結尾
	filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error {  // 遍歷路徑下所有子孫目錄
		fmt.Println(path, fileInfo.Name())
		return nil
	})
}

3、判斷檔案是否存在

主要是通過 os 包的 open 方法開啟檔案,並接收返回的錯誤資訊,給 os 包的 IsNotExist 函式判斷,返回一個布林值

package main

import (
	"fmt"
	"os"
)

func main()  {
	file, err := os.Open("xxx")
	if err != nil {
		if os.IsNotExist(err){
			fmt.Println("檔案不存在")
		}
	} else {
		file.Close()
	}
}

4、獲取檔案的資訊

獲取檔案資訊及資料夾的子檔案資訊

lstat :如果是超連結,獲取的是超連結的資訊

stat :如果是超連結,獲取的是目標檔案的資訊

package main

import (
	"fmt"
	"os"
)

func main()  {
	for _, path := range []string{"user.txt", "reader.go", "aaa"} {  // 迴圈檔名
		fileInfo, err := os.Stat(path)
		if err != nil {
			if os.IsNotExist(err){
				fmt.Println("檔案不存在")
			}
		} else {
			fmt.Println(fileInfo.Name(), fileInfo.IsDir(), fileInfo.Size(), fileInfo.ModTime())
			//檔名	是否是目錄	大小	修改時間
			//user.txt false 12 2021-08-30 16:20:54.853211783 +0800 CST
			//reader.go false 387 2021-08-30 15:57:06.860696025 +0800 CST
			if fileInfo.IsDir() {  // 獲取目錄下的子目錄及檔案
				dirfile, err := os.Open(path)
				if err == nil {
					defer dirfile.Close()
					//childrens, _ := dirfile.Readdir(-1)  // 獲取所有的子目錄
					//for _, children := range childrens {
					//	fmt.Println(children.Name(), children.IsDir(), children.Size(), children.ModTime())
					//}
					names, _ := dirfile.Readdirnames(-1)
					for _, name := range names {
						fmt.Println(name)
					}
				}
			}
		}
	}
}

5、拷貝檔案

copyfile 功能的實現,主要藉助於 golang 自帶的命令列解析工具 flag (這個在後面的文章中會專門介紹),通過 bufio 讀取並寫入檔案

package main

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

func copyfile(src, dest string) {
	srcfile, err := os.Open(src)
	if err != nil {
		fmt.Println(err)
	} else {
		defer srcfile.Close()
		destfile, err := os.Create(dest)
		if err != nil {
			fmt.Println(err)
		} else {
			defer destfile.Close()
			reader := bufio.NewReader(srcfile)
			writer := bufio.NewWriter(destfile)
			bytes := make([]byte, 1024*1024*10)  // 緩衝區大小
			for {
				n, err := reader.Read(bytes)
				if err != nil {
					if err != io.EOF {
						fmt.Println(err)
					}
					break
				}
				writer.Write(bytes[:n])
				//destfile.Write(bytes[:n])
				writer.Flush()
			}
		}
	}
}

func main() {
	src := flag.String("s", "", "src file")
	dest := flag.String("d", "", "dest file")
	help := flag.Bool("h", false, "help")

	flag.Usage = func() {
		fmt.Println(`
Usage: copyfile -s srcfile -d destfile
Options:
		`)
		flag.PrintDefaults()
	}

	flag.Parse()
	if *help || *src == "" || *dest == "" {
		flag.Usage()
	} else {
		copyfile(*src, *dest)
	}
}

支援遞迴拷貝目錄及目錄下的檔案

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

func copyFile(src, dest string) {
	srcFile, err := os.Open(src)
	if err != nil {
		fmt.Println(err)
	} else {
		defer srcFile.Close()
		destFile, err := os.Create(dest)
		if err != nil {
			fmt.Println(err)
		} else {
			defer destFile.Close()
			reader := bufio.NewReader(srcFile)
			writer := bufio.NewWriter(destFile)
			bytes := make([]byte, 1024*1024*10)  // 緩衝區大小
			for {
				n, err := reader.Read(bytes)
				if err != nil {
					if err != io.EOF {
						fmt.Println(err)
					}
					break
				}
				writer.Write(bytes[:n])
				//destfile.Write(bytes[:n])
				writer.Flush()
			}
		}
	}
}

func copyDir(src, dst string) {
	files, err := ioutil.ReadDir(src)
	if err == nil {
		os.Mkdir(dst, os.ModePerm)  // 目標路徑不存在先建立目錄
		for _, file := range files {
			if file.IsDir() {  // 目錄 遞迴呼叫
				copyDir(filepath.Join(src, file.Name()), filepath.Join(dst, file.Name()))
			} else {  // 檔案 拷貝檔案
				copyFile(filepath.Join(src, file.Name()), filepath.Join(dst, file.Name()))
			}
		}
	}
}

func main() {
	src := flag.String("s", "", "src file")
	dest := flag.String("d", "", "dest file")
	help := flag.Bool("h", false, "help")

	flag.Usage = func() {
		fmt.Println(`
Usage: copyfile -s srcfile -d destfile
Options:
		`)
		flag.PrintDefaults()
	}

	flag.Parse()
	if *help || *src == "" || *dest == "" {
		flag.Usage()
	} else {
		// src是否存在 不存在則退出
		// src 檔案 copyfile
		// dst 判斷 存在 退出

		// src 目錄 copydir
		// dst 判斷 不存在 copy
		// dst 存在 目錄
		// dst 存在 不是目錄 退出
		if _, err := os.Stat(*dest); err == nil {
			fmt.Println("目的檔案已存在")
			return
		} else {
			if !os.IsNotExist(err) {
				fmt.Println("目的檔案獲取錯誤", err)
			}
		}
		if info, err := os.Stat(*src); err != nil {
			if os.IsNotExist(err) {
				fmt.Println("原始檔不存在")
			} else {
				fmt.Println("原始檔獲取錯誤: ", err)
			}
		} else {
			if info.IsDir() {
				copyDir(*src, *dest)
			} else {
				copyFile(*src, *dest)
			}
		}
	}
}

6、目錄操作

目錄的建立、刪除和重新命名

package main

import "os"

func main()  {
	os.Mkdir("test01", 0644)
	os.Rename("test01", "test02")
	os.Remove("test02")
}

建立子目錄,當父目錄不存在的時候需要使用 mkdirAll ,並設定許可權(建立目錄除了給定的許可權還要加上系統的 UmaskGo 也是如實遵循這種約定, Umask 是許可權的補碼,用於設定建立檔案和資料夾預設許可權的)

package main

import (
	"os"
	"syscall"
)

func main()  {
	//os.Mkdir("test01", 0644)  // 如果目錄存在會報錯
	//os.Rename("test01", "test02")
	//os.Remove("test02")

	//err := os.Mkdir("test01/xxx", 0644)
	//fmt.Println(err)  // mkdir test01/xxx: no such file or directory
	mask := syscall.Umask(0)    // 改為 0000 八進位制
	defer syscall.Umask(mask)   // 改為原來的 umask
	err := os.MkdirAll("test01/test/", 0777)  // MkdirAll建立
	if err != nil {
		panic(err)
	}
	os.RemoveAll("test01")
}

7、常見目錄

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.TempDir())  // 臨時目錄
	fmt.Println(os.UserCacheDir())  // 使用者快取目錄
	fmt.Println(os.UserHomeDir())  // 使用者家目錄
	fmt.Println(os.Getwd())  // 當前絕對目錄
}

See you ~