Golang檔案操作整理

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

最近做的一點事情,用到了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)//沒錯或者路徑不存在 直接返回 nilif 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 mainimport ("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 []uintfmt.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 mainimport ("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.FileInfofor _, 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 writergw := gzip.NewWriter(fw)defer gw.Close()//tar writetw := 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")}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.