最近做的一點事情,用到了golang中不少文件操作的相關內容,創建,刪除,遍歷,壓縮之類的,這裡整理整理,希望能掌握的系統一點,把模糊的地方理清楚。
基本操作
檔案建立
建立檔案的時候,一定要注意權限問題,一般預設的檔案權限是0666 關於權限的相關內容,具體可以參考鳥叔p141 這裡還是再回顧下,文件屬性r w x r w x r w x,第一位是文件屬性,一般常用的"-" 表示的是普通文件,"d"表示的是目錄,golang裡面使用os.Create
建立檔案的時候似乎只能使用0xxx的形式。例如0666就表示創建了一個普通文件,文件所有者的權限,文件所屬用戶組的權限,以及其他人對此文件的權限都是110表示可讀可寫,不可執行。
文件刪除
文件刪除的時候,不管是普通文件還是目錄文件,都可以用err:=os.Remove(filename)
這樣的操作來執行。當然要移除整個資料夾,直接使用RemoveAll(path string)
操作即可。可以看一下RemoveAll函數的內部實現,整體上就是遍歷,遞歸的操作過程,其他的類似的文件操作都可以用類似的模板來實現,下面以RemoveAll函數為模板,進行一下具體的分析,注意考慮到各種情況:
func RemoveAll(path string) error { // Simple case: if Remove works, we're done. //先尝试一下remove如果是普通文件 直接删掉 报错 则可能是目录中还有子文件 err := Remove(path) //没错或者路径不存在 直接返回 nil if err == nil || IsNotExist(err) { return nil } // Otherwise, is this a directory we need to recurse into? // 目录里面还有文件 需要递归处理 // 注意Lstat和stat函数的区别,两个都是返回文件的状态信息 //Lstat多了处理Link文件的功能,会返回Linked文件的信息,而state直接返回的是Link文件所指向的文件的信息 dir, serr := Lstat(path) if serr != nil { if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { return nil } return serr } //不是目录 if !dir.IsDir() { // Not a directory; return the error from Remove. return err } // Directory. fd, err := Open(path) if err != nil { if IsNotExist(err) { // Race. It was deleted between the Lstat and Open. // Return nil per RemoveAll's docs. return nil } return err } // Remove contents & return first error. err = nil //递归遍历目录中的文件 如果参数n<=0则将全部的信息存入到一个slice中返回 //如果参数n>0则至多返回n个元素的信息存入到slice当中 //还有一个类似的函数是Readdir 这个返回的是 目录中的内容的Fileinfo信息 for { names, err1 := fd.Readdirnames(100) for _, name := range names { err1 := RemoveAll(path + string(PathSeparator) + name) if err == nil { err = err1 } } //遍历到最后一个位置 if err1 == io.EOF { break } // If Readdirnames returned an error, use it. if err == nil { err = err1 } if len(names) == 0 { break } } // Close directory, because windows won't remove opened directory. fd.Close() //递归结束 当前目录下位空 删除当前目录 // Remove directory. err1 := Remove(path) if err1 == nil || IsNotExist(err1) { return nil } if err == nil { err = err1 } return err }
檔案狀態
從檔案中寫入寫出內容
這部分較多的涉及I/O的相關操作,系統的介紹放在I/O那部分來整理,大體上向文件中讀寫內容的時候有三種方式:
1、在使用f, err := os.Open(file_path)
開啟檔案之後直接使用f.read() f.write()
結合自訂的buffer每次從檔案中讀入/讀出固定的內容
2、使用ioutl的readFile和writeFile方法
3、使用bufio採用帶有快取的方式進行讀寫,例如透過info:=bufio.NewReader(f)
將實現了io.Reader的介面的實例載入上來之後,就可以使用info.ReadLine()來每次實作一整行的讀取,直到err資訊為io.EOF時,讀取結束
這個blog對三種檔案操作的讀入速度進行了比較,似乎讀取大檔案的時候採用ioutil的時候效率要高些。
每種方式都有不同的適用情況,以下是分別用三種方式進行讀出操作的例子,對於寫入檔案的操作,可以參考讀出操作來進行:
package main import ( "bufio" "fmt" "io" "io/ioutil" "os" ) func check(e error) { if e != nil { panic(e) } } func main() { //查看当前的工作目录路径 得到测试文件的绝对路径 current_dir, _ := os.Getwd() fmt.Println(current_dir) file_path := current_dir + "/temp.txt" //方式一: //通过ioutil直接通过文件名来加载文件 //一次将整个文件加载进来 粒度较大 err返回为nil的时候 文件会被成功加载 dat, err := ioutil.ReadFile(file_path) //若加载的是一个目录 会返回[]os.FileInfo的信息 //ioutil.ReadDir() check(err) //the type of data is []uint fmt.Println(dat) //将文件内容转化为string输出 fmt.Println(string(dat)) //方式二: //通过os.Open的方式得到 *File 类型的变量 //貌似是一个指向这个文件的指针 通过这个指针 可以对文件进行更细粒度的操作 f, err := os.Open(file_path) check(err) //手工指定固定大小的buffer 每次通过buffer来 进行对应的操作 buffer1 := make([]byte, 5) //从文件f中读取len(buffer1)的信息到buffer1中 返回值n1是读取的byte的长度 n1, err := f.Read(buffer1) check(err) fmt.Printf("%d bytes: %s\n", n1, string(buffer1)) //通过f.seek进行更精细的操作 第一个参数表示offset为6 第二个参数表示文件起始的相对位置 //之后再读就从o2位置开始往后读信息了 o2, err := f.Seek(6, 0) check(err) buffer2 := make([]byte, 2) //读入了n2长度的信息到buffer2中 n2, err := f.Read(buffer2) check(err) fmt.Printf("%d bytes after %d position : %s\n", n2, o2, string(buffer2)) //通过io包种的函数 也可以实现类似的功能 o3, err := f.Seek(6, 0) check(err) buffer3 := make([]byte, 2) n3, err := io.ReadAtLeast(f, buffer3, len(buffer3)) check(err) fmt.Printf("%d bytes after %d position : %s\n", n3, o3, string(buffer3)) //方式三 //通过bufio包来进行读取 bufio中又许多比较有用的函数 比如一次读入一整行的内容 //调整文件指针的起始位置到最开始的地方 _, err = f.Seek(10, 0) check(err) r4 := bufio.NewReader(f) //读出从头开始的5个字节 b4, err := r4.Peek(5) check(err) //fmt.Println(string(b4)) fmt.Printf("5 bytes : %s\n", string(b4)) //调整文件到另一个地方 _, err = f.Seek(0, 0) check(err) r5 := bufio.NewReader(f) //读出从指针所指位置开始的5个字节 b5, err := r5.Peek(5) check(err) //fmt.Println(string(b4)) fmt.Printf("5 bytes : %s\n", string(b5)) //测试bufio的其他函数 for { //读出内容保存为string 每次读到以'\n'为标记的位置 line, err := r5.ReadString('\n') fmt.Print(line) if err == io.EOF { break } } //ReadLine() ReadByte() 的用法都是类似 一般都是当err为io.EOF的时候 //读入内容就结束 //感觉实际用的时候 还是通过方式三比较好 粒度正合适 还有多种处理输入的方式 f.Close() }
進階操作
檔案打包,檔案解壓縮,檔案遍歷,這些相關的操作基本上都可以參考RemoveAll的方式來進行,就是遞歸加遍歷的方式。
下面是檔案壓縮的一個實作:
//将文件夹中的内容打包成 .gz.tar 文件 package main import ( "archive/tar" "compress/gzip" "fmt" "io" "os" ) //将fi文件的内容 写入到 dir 目录之下 压缩到tar文件之中 func Filecompress(tw *tar.Writer, dir string, fi os.FileInfo) { //打开文件 open当中是 目录名称/文件名称 构成的组合 filename := dir + "/" + fi.Name() fmt.Println("the last one:", filename) fr, err := os.Open(filename) fmt.Println(fr.Name()) if err != nil { panic(err) } defer fr.Close() hdr, err := tar.FileInfoHeader(fi, "") hdr.Name = fr.Name() if err = tw.WriteHeader(hdr); err != nil { panic(err) } //bad way // //信息头部 生成tar文件的时候要先写入tar结构体 // h := new(tar.Header) // //fmt.Println(reflect.TypeOf(h)) // h.Name = fi.Name() // h.Size = fi.Size() // h.Mode = int64(fi.Mode()) // h.ModTime = fi.ModTime() // //将信息头部的内容写入 // err = tw.WriteHeader(h) // if err != nil { // panic(err) // } //copy(dst Writer,src Reader) _, err = io.Copy(tw, fr) if err != nil { panic(err) } //打印文件名称 fmt.Println("add the file: " + fi.Name()) } //将目录中的内容递归遍历 写入tar 文件中 func Dircompress(tw *tar.Writer, dir string) { fmt.Println(dir) //打开文件夹 dirhandle, err := os.Open(dir + "/") //fmt.Println(dir.Name()) //fmt.Println(reflect.TypeOf(dir)) if err != nil { panic(err) } defer dirhandle.Close() fis, err := dirhandle.Readdir(0) //fis的类型为 []os.FileInfo //也可以通过Readdirnames来读入所有子文件的名称 //但是这样 再次判断是否为文件的时候 需要通过Stat来得到文件的信息 //返回的就是os.File的类型 if err != nil { panic(err) } //遍历文件列表 每一个文件到要写入一个新的*tar.Header //var fi os.FileInfo for _, fi := range fis { fmt.Println(fi.Name()) if fi.IsDir() { newname := dir + "/" + fi.Name() fmt.Println("using dir") fmt.Println(newname) //这个样直接continue就将所有文件写入到了一起 没有层级结构了 //Filecompress(tw, dir, fi) Dircompress(tw, newname) } else { //如果是普通文件 直接写入 dir 后面已经有了 / Filecompress(tw, dir, fi) } } } //在tardir目录中创建一个.tar.gz文件 存放压缩之后的文件 func Dirtotar(sourcedir string, tardir string, tarname string) { //file write 在tardir目录下创建 fw, err := os.Create(tardir + "/" + tarname + ".tar.gz") //type of fw is *os.File // fmt.Println(reflect.TypeOf(fw)) if err != nil { panic(err) } defer fw.Close() //gzip writer gw := gzip.NewWriter(fw) defer gw.Close() //tar write tw := tar.NewWriter(gw) fmt.Println("源目录:", sourcedir) Dircompress(tw, sourcedir) //通过控制写入流 也可以控制 目录结构 比如将当前目录下的Dockerfile文件单独写在最外层 fileinfo, err := os.Stat("tarrepo" + "/" + "testDockerfile") fmt.Println("the file name:", fileinfo.Name()) if err != nil { panic(err) } //比如这里将Dockerfile放在 tar包中的最外层 会注册到tar包中的 /tarrepo/testDockerfile 中 Filecompress(tw, "tarrepo", fileinfo) //Filecompress(tw, "systempdir/test_testwar_tar/", fileinfo) fmt.Println("tar.gz packaging OK") } func main() { // workdir, _ := os.Getwd() // fmt.Println(workdir) Dirtotar("testdir", "tarrepo", "testtar") }
補充一下
之前可能也沒有註意到OpenFile函數與Open函數的區別Openfile函數可以指定傳回的檔案描述子的權限,透過O_RDONLY、O_WRONLY、O_RDWR 等等來控制。而Open函數在其內部是呼叫OpenFile函數的,預設的情況是O_RDONLY權限,如果僅用Open函數傳回檔案描述符,之後再對檔案寫操作的話,就會傳回bad file descriptor 的錯誤,這個還是應該多留意一下的,細節問題要弄仔細,本質上來說是os中的文件描述符的問題。
新增檔案拷貝的動作
refer to this :https://www.socketloop.com/tutorials/golang-copy-directory-including-sub-directories-files
轉載於:https://www.cnblogs.com/Goden/p/4533908.html
更多golang相關技術文章,請造訪golang#教程欄!
以上是關於Golang文件操作的整理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

go语言有缩进。在go语言中,缩进直接使用gofmt工具格式化即可(gofmt使用tab进行缩进);gofmt工具会以标准样式的缩进和垂直对齐方式对源代码进行格式化,甚至必要情况下注释也会重新格式化。

本篇文章带大家了解一下golang 的几种常用的基本数据类型,如整型,浮点型,字符,字符串,布尔型等,并介绍了一些常用的类型转换操作。

go语言叫go的原因:想表达这门语言的运行速度、开发速度、学习速度(develop)都像gopher一样快。gopher是一种生活在加拿大的小动物,go的吉祥物就是这个小动物,它的中文名叫做囊地鼠,它们最大的特点就是挖洞速度特别快,当然可能不止是挖洞啦。

是,TiDB采用go语言编写。TiDB是一个分布式NewSQL数据库;它支持水平弹性扩展、ACID事务、标准SQL、MySQL语法和MySQL协议,具有数据强一致的高可用特性。TiDB架构中的PD储存了集群的元信息,如key在哪个TiKV节点;PD还负责集群的负载均衡以及数据分片等。PD通过内嵌etcd来支持数据分布和容错;PD采用go语言编写。

在写 Go 的过程中经常对比这两种语言的特性,踩了不少坑,也发现了不少有意思的地方,下面本篇就来聊聊 Go 自带的 HttpClient 的超时机制,希望对大家有所帮助。

go语言需要编译。Go语言是编译型的静态语言,是一门需要编译才能运行的编程语言,也就说Go语言程序在运行之前需要通过编译器生成二进制机器码(二进制的可执行文件),随后二进制文件才能在目标机器上运行。

删除map元素的两种方法:1、使用delete()函数从map中删除指定键值对,语法“delete(map, 键名)”;2、重新创建一个新的map对象,可以清空map中的所有元素,语法“var mapname map[keytype]valuetype”。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境