中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Golang文件操作的方法有哪些

發布時間:2021-06-28 16:14:16 來源:億速云 閱讀:190 作者:chen 欄目:編程語言

這篇文章主要講解了“Golang文件操作的方法有哪些”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Golang文件操作的方法有哪些”吧!

最近做的一點事情,用到了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

感謝各位的閱讀,以上就是“Golang文件操作的方法有哪些”的內容了,經過本文的學習后,相信大家對Golang文件操作的方法有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

峨眉山市| 万安县| 东乌| 铜陵市| 浙江省| 林甸县| 虎林市| 隆子县| 漳浦县| 合水县| 海伦市| 弋阳县| 石河子市| 体育| 宁海县| 同江市| 阿拉善盟| 日照市| 衡阳县| 习水县| 岑巩县| 马边| 屏边| 贵德县| 宁蒗| 日喀则市| 冕宁县| 芜湖市| 米脂县| 仙桃市| 莆田市| 博客| 湖口县| 兴仁县| 岱山县| 乾安县| 墨竹工卡县| 长子县| 全州县| 新竹县| 汤原县|