Golang讀寫檔案操作

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

最近在使用Golang進行檔案讀寫的過程中,遇到幾個細節問題導致程式寫入資料時有一定髒資料的殘留,最後發現是使用os.OpenFile在進行檔案操作的時候沒有使用正確的flag造成的。因此專門去學習了下Golang中讀寫檔案的幾種方式方法,在此記錄下一些簡單的操作,防止以後遺忘。

讀檔案

使用golang語言去讀取一個檔案預設會有多種方式,這裡主要介紹以下幾種。

使用ioutil直接讀取

需要引入io/ioutil包,該包預設擁有以下函數供使用者調用。

func NopCloser(r io.Reader) io.ReadCloserfunc ReadAll(r io.Reader) ([]byte, error)func ReadDir(dirname string) ([]os.FileInfo, error)func ReadFile(filename string) ([]byte, error)func TempDir(dir, prefix string) (name string, err error)func TempFile(dir, prefix string) (f *os.File, err error)func WriteFile(filename string, data []byte, perm os.FileMode) error

讀檔案,我們可以看以下三個函數:

//從一個io.Reader類型中讀取內容直到返回錯誤或者EOF時返回讀取的資料,當err == nil時,資料成功讀取到[]byte中//ReadAll函數被定義為從源中讀取資料直到EOF,它是不會去從返回資料中去判斷EOF來作為讀取成功的依據func ReadAll(r io.Reader) ([]byte, error)//讀取一個目錄,並返回一個目前的目錄下的檔案對象列表和錯誤資訊func ReadDir(dirname string) ([]os.FileInfo, error)//讀取檔案內容,並返回[]byte資料和錯誤資訊。err == nil時,讀取成功func ReadFile(filename string) ([]byte, error)

讀取檔案樣本:

$ cat readfile.gopackage mainimport (    "fmt"    "io/ioutil"    "strings")func main() {   Ioutil("mytestfile.txt")    }func Ioutil(name string) {    if contents,err := ioutil.ReadFile(name);err == nil {        //因為contents是[]byte類型,直接轉換成string類型後會多一行空格,需要使用strings.Replace替換分行符號        result := strings.Replace(string(contents),"\n","",1)        fmt.Println(result)        }    }$ go run readfile.goxxbandy.github.io @by Andy_xu

藉助os.Open進行讀取檔案

由於os.Open是開啟一個檔案並返回一個檔案對象,因此其實可以結合ioutil.ReadAll(r io.Reader)來進行讀取。io.Reader其實是一個包含Read方法的介面類型,而檔案對象本身是實現了了Read方法的。

我們先來看下os.Open家族的相關函數

//開啟一個需要被讀取的檔案,如果成功讀取,返回的檔案對象將可用被讀取,該函數預設的許可權為O_RDONLY,也就是只對檔案有唯讀許可權。如果有錯誤,將返回*PathError類型func Open(name string) (*File, error)//大部分使用者會選擇該函數來代替Open or Create函數。該函數主要用來指定參數(os.O_APPEND|os.O_CREATE|os.O_WRONLY)以及檔案許可權(0666)來開啟檔案,如果開啟成功返回的檔案對象將被用作I/O操作func OpenFile(name string, flag int, perm FileMode) (*File, error)

使用os.Open家族函數和ioutil.ReadAll()讀取檔案樣本:

func OsIoutil(name string) {      if fileObj,err := os.Open(name);err == nil {      //if fileObj,err := os.OpenFile(name,os.O_RDONLY,0644); err == nil {        defer fileObj.Close()        if contents,err := ioutil.ReadAll(fileObj); err == nil {            result := strings.Replace(string(contents),"\n","",1)            fmt.Println("Use os.Open family functions and ioutil.ReadAll to read a file contents:",result)            }        }}# 在main函數中調用OsIoutil(name)函數就可以讀取檔案內容了$ go run readfile.goUse os.Open family functions and ioutil.ReadAll to read a file contents: xxbandy.github.io @by Andy_xu

然而上述方式會比較繁瑣一些,因為使用了os的同時藉助了ioutil,但是在讀取大檔案的時候還是比較有優勢的。不過讀取小檔案可以直接使用檔案對象的一些方法。

不論是上邊說的os.Open還是os.OpenFile他們最終都返回了一個*File檔案對象,而該檔案對象預設是有很多方法的,其中讀取檔案的方法有如下幾種:

//從檔案對象中讀取長度為b的位元組,返回當前讀到的位元組數以及錯誤資訊。因此使用該方法需要先初始化一個符合內容大小的空的位元組列表。讀取到檔案的末尾時,該方法返回0,io.EOFfunc (f *File) Read(b []byte) (n int, err error)//從檔案的off位移量開始讀取長度為b的位元組。返回讀取到位元組數以及錯誤資訊。當讀取到的位元組數n小於想要讀取位元組的長度len(b)的時候,該方法將返回非空的error。當讀到檔案末尾時,err返回io.EOFfunc (f *File) ReadAt(b []byte, off int64) (n int, err error)

使用檔案對象的Read方法讀取:

func FileRead(name string) {    if fileObj,err := os.Open(name);err == nil {        defer fileObj.Close()        //在定義空的byte列表時盡量大一些,否則這種方式讀取內容可能造成檔案讀取不完整        buf := make([]byte, 1024)        if n,err := fileObj.Read(buf);err == nil {               fmt.Println("The number of bytes read:"+strconv.Itoa(n),"Buf length:"+strconv.Itoa(len(buf)))               result := strings.Replace(string(buf),"\n","",1)               fmt.Println("Use os.Open and File's Read method to read a file:",result)            }    }}

使用os.Openbufio.Reader讀取檔案內容

bufio包實現了緩衝IO,它本身封裝了io.Readerio.Writer對象,建立了另外的Reader和Writer對象,不過該種方式是帶有緩衝的,因此對於文本I/O來說,該包是提供了一些便利的。

先看下bufio模組下的相關的Reader函數方法:

//首先定義了一個用來緩衝io.Reader對象的結構體,同時該結構體擁有以下相關的方法type Reader struct {}//NewReader函數用來返回一個預設大小buffer的Reader對象(預設大小好像是4096) 等同於NewReaderSize(rd,4096)func NewReader(rd io.Reader) *Reader//該函數返回一個指定大小buffer(size最小為16)的Reader對象,如果 io.Reader參數已經是一個足夠大的Reader,它將返回該Readerfunc NewReaderSize(rd io.Reader, size int) *Reader//該方法返回從當前buffer中能被讀到的位元組數func (b *Reader) Buffered() int//Discard方法跳過後續的 n 個位元組的資料,返回跳過的位元組數。如果0 <= n <= b.Buffered(),該方法將不會從io.Reader中成功讀取資料。func (b *Reader) Discard(n int) (discarded int, err error)//Peekf方法返回緩衝的一個切片,該切片只包含緩衝中的前n個位元組的資料func (b *Reader) Peek(n int) ([]byte, error)//把Reader緩衝對象中的資料讀入到[]byte類型的p中,並返回讀取的位元組數。讀取成功,err將返回空值func (b *Reader) Read(p []byte) (n int, err error)//返回單個位元組,如果沒有資料返回errfunc (b *Reader) ReadByte() (byte, error)//該方法在b中讀取delimz之前的所有資料,返回的切片是已讀出的資料的引用,切片中的資料在下一次的讀取操作之前是有效。如果未找到delim,將返回尋找結果並返回nil空值。因為緩衝的資料可能被下一次的讀寫操作修改,因此一般使用ReadBytes或者ReadString,他們返回的都是資料拷貝func (b *Reader) ReadSlice(delim byte) (line []byte, err error)//功能同ReadSlice,返回資料的拷貝func (b *Reader) ReadBytes(delim byte) ([]byte, error)//功能同ReadBytes,返回字串func (b *Reader) ReadString(delim byte) (string, error)//該方法是一個低水平的讀取方式,一般建議使用ReadBytes('\n') 或 ReadString('\n'),或者使用一個 Scanner來代替。ReadLine 通過調用 ReadSlice 方法實現,返回的也是緩衝的切片,用於讀取一行資料,不包括行尾標記(\n 或 \r\n)func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)//讀取單個UTF-8字元並返回一個rune和位元組大小func (b *Reader) ReadRune() (r rune, size int, err error)

樣本:

func BufioRead(name string) {    if fileObj,err := os.Open(name);err == nil {        defer fileObj.Close()        //一個檔案對象本身是實現了io.Reader的 使用bufio.NewReader去初始化一個Reader對象,存在buffer中的,讀取一次就會被清空        reader := bufio.NewReader(fileObj)        //使用ReadString(delim byte)來讀取delim以及之前的資料並返回相關的字串.        if result,err := reader.ReadString(byte('@'));err == nil {            fmt.Println("使用ReadSlince相關方法讀取內容:",result)        }        //注意:上述ReadString已經將buffer中的資料讀取出來了,下面將不會輸出內容        //需要注意的是,因為是將檔案內容讀取到[]byte中,因此需要對大小進行一定的把控        buf := make([]byte,1024)        //讀取Reader對象中的內容到[]byte類型的buf中        if n,err := reader.Read(buf); err == nil {            fmt.Println("The number of bytes read:"+strconv.Itoa(n))            //這裡的buf是一個[]byte,因此如果需要只輸出內容,仍然需要將檔案內容的分行符號替換掉            fmt.Println("Use bufio.NewReader and os.Open read file contents to a []byte:",string(buf))        }    }}

讀檔案所有方式樣本

/** * @File Name: readfile.go * @Author: * @Email: * @Create Date: 2017-12-16 16:12:01 * @Last Modified: 2017-12-17 12:12:02 * @Description:讀取指定檔案的幾種方法,需要注意的是[]byte類型在轉換成string類型的時候,都會在最後多一行空格,需要使用result := strings.Replace(string(contents),"\n","",1) 方式替換分行符號 */package mainimport (    "fmt"    "io/ioutil"    "strings"    "os"    "strconv"    "bufio")func main() {   Ioutil("mytestfile.txt")   OsIoutil("mytestfile.txt")   FileRead("mytestfile.txt")   BufioRead("mytestfile.txt")    }func Ioutil(name string) {    if contents,err := ioutil.ReadFile(name);err == nil {        //因為contents是[]byte類型,直接轉換成string類型後會多一行空格,需要使用strings.Replace替換分行符號        result := strings.Replace(string(contents),"\n","",1)        fmt.Println("Use ioutil.ReadFile to read a file:",result)        }    }func OsIoutil(name string) {      if fileObj,err := os.Open(name);err == nil {      //if fileObj,err := os.OpenFile(name,os.O_RDONLY,0644); err == nil {        defer fileObj.Close()        if contents,err := ioutil.ReadAll(fileObj); err == nil {            result := strings.Replace(string(contents),"\n","",1)            fmt.Println("Use os.Open family functions and ioutil.ReadAll to read a file :",result)            }        }}func FileRead(name string) {    if fileObj,err := os.Open(name);err == nil {        defer fileObj.Close()        //在定義空的byte列表時盡量大一些,否則這種方式讀取內容可能造成檔案讀取不完整        buf := make([]byte, 1024)        if n,err := fileObj.Read(buf);err == nil {               fmt.Println("The number of bytes read:"+strconv.Itoa(n),"Buf length:"+strconv.Itoa(len(buf)))               result := strings.Replace(string(buf),"\n","",1)               fmt.Println("Use os.Open and File's Read method to read a file:",result)            }    }}func BufioRead(name string) {    if fileObj,err := os.Open(name);err == nil {        defer fileObj.Close()        //一個檔案對象本身是實現了io.Reader的 使用bufio.NewReader去初始化一個Reader對象,存在buffer中的,讀取一次就會被清空        reader := bufio.NewReader(fileObj)        //使用ReadString(delim byte)來讀取delim以及之前的資料並返回相關的字串.        if result,err := reader.ReadString(byte('@'));err == nil {            fmt.Println("使用ReadSlince相關方法讀取內容:",result)        }        //注意:上述ReadString已經將buffer中的資料讀取出來了,下面將不會輸出內容        //需要注意的是,因為是將檔案內容讀取到[]byte中,因此需要對大小進行一定的把控        buf := make([]byte,1024)        //讀取Reader對象中的內容到[]byte類型的buf中        if n,err := reader.Read(buf); err == nil {            fmt.Println("The number of bytes read:"+strconv.Itoa(n))            //這裡的buf是一個[]byte,因此如果需要只輸出內容,仍然需要將檔案內容的分行符號替換掉            fmt.Println("Use bufio.NewReader and os.Open read file contents to a []byte:",string(buf))        }    }}

寫檔案

那麼上述幾種方式來讀取檔案的方式也支援檔案的寫入,相關的方法如下:

使用ioutil包進行檔案寫入

// 寫入[]byte類型的data到filename檔案中,檔案許可權為permfunc WriteFile(filename string, data []byte, perm os.FileMode) error

樣本:

$ cat writefile.go/** * @File Name: writefile.go * @Author: * @Email: * @Create Date: 2017-12-17 12:12:09 * @Last Modified: 2017-12-17 12:12:30 * @Description:使用多種方式將資料寫入檔案 */package mainimport (    "fmt"    "io/ioutil")func main() {      name := "testwritefile.txt"      content := "Hello, xxbandy.github.io!\n"      WriteWithIoutil(name,content)}//使用ioutil.WriteFile方式寫入檔案,是將[]byte內容寫入檔案,如果content字串中沒有分行符號的話,預設就不會有分行符號func WriteWithIoutil(name,content string) {    data :=  []byte(content)    if ioutil.WriteFile(name,data,0644) == nil {        fmt.Println("寫入檔案成功:",content)        }    }# 會有分行符號$ go run writefile.go寫入檔案成功: Hello, xxbandy.github.io!

使用os.Open相關函數進行檔案寫入

因為os.Open系列的函數會開啟檔案,並返回一個檔案對象指標,而該檔案對象是一個定義的結構體,擁有一些相關寫入的方法。

檔案對象結構體以及相關寫入檔案的方法:

//寫入長度為b位元組切片到檔案f中,返回寫入位元組號和錯誤資訊。當n不等於len(b)時,將返回非空的errfunc (f *File) Write(b []byte) (n int, err error)//在off位移量出向檔案f寫入長度為b的位元組func (f *File) WriteAt(b []byte, off int64) (n int, err error)//類似於Write方法,但是寫入內容是字串而不是位元組切片func (f *File) WriteString(s string) (n int, err error)

注意:使用WriteString()j進行檔案寫入發現經常新內容寫入時無法正常覆蓋全部新內容。(是因為字串長度不一樣)

樣本:

//使用os.OpenFile()相關函數開啟檔案對象,並使用檔案對象的相關方法進行檔案寫入操作func WriteWithFileWrite(name,content string){    fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_TRUNC,0644)    if err != nil {        fmt.Println("Failed to open the file",err.Error())        os.Exit(2)    }    defer fileObj.Close()    if _,err := fileObj.WriteString(content);err == nil {        fmt.Println("Successful writing to the file with os.OpenFile and *File.WriteString method.",content)    }    contents := []byte(content)    if _,err := fileObj.Write(contents);err == nil {        fmt.Println("Successful writing to thr file with os.OpenFile and *File.Write method.",content)    }}

注意:使用os.OpenFile(name string, flag int, perm FileMode)開啟檔案並進行檔案內容更改,需要注意flag相關的參數以及含義。

const (        O_RDONLY int = syscall.O_RDONLY // 唯讀開啟檔案和os.Open()同義        O_WRONLY int = syscall.O_WRONLY // 唯寫開啟檔案        O_RDWR   int = syscall.O_RDWR   // 讀寫方式開啟檔案        O_APPEND int = syscall.O_APPEND // 當寫的時候使用追加模式到檔案末尾        O_CREATE int = syscall.O_CREAT  // 如果檔案不存在,此案建立        O_EXCL   int = syscall.O_EXCL   // 和O_CREATE一起使用, 只有當檔案不存在時才建立        O_SYNC   int = syscall.O_SYNC   // 以同步I/O方式開啟檔案,直接寫入硬碟.        O_TRUNC  int = syscall.O_TRUNC  // 如果可以的話,當開啟檔案時先清空檔案)

使用io包中的相關函數寫入檔案

io包中有一個WriteString()函數,用來將字串寫入一個Writer對象中。

//將字串s寫入w(可以是一個[]byte),如果w實現了一個WriteString方法,它可以被直接調用。否則w.Write會再一次被調用func WriteString(w Writer, s string) (n int, err error)//Writer對象的定義type Writer interface {        Write(p []byte) (n int, err error)}

樣本:

//使用io.WriteString()函數進行資料的寫入func WriteWithIo(name,content string) {    fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_APPEND,0644)    if err != nil {        fmt.Println("Failed to open the file",err.Error())        os.Exit(2)    }    if  _,err := io.WriteString(fileObj,content);err == nil {        fmt.Println("Successful appending to the file with os.OpenFile and io.WriteString.",content)    }}

使用bufio包中的相關函數寫入檔案

bufioio包中很多操作都是相似的,唯一不同的地方是bufio提供了一些緩衝的操作,如果對檔案I/O操作比較頻繁的,使用bufio還是能增加一些效能的。

bufio包中,有一個Writer結構體,而其相關的方法也支援一些寫入操作。

//Writer是一個空的結構體,一般需要使用NewWriter或者NewWriterSize來初始化一個結構體對象type Writer struct {        // contains filtered or unexported fields}//NewWriterSize和NewWriter函數//返回預設緩衝大小的Writer對象(預設是4096)func NewWriter(w io.Writer) *Writer//指定緩衝大小建立一個Writer對象func NewWriterSize(w io.Writer, size int) *Writer//Writer對象相關的寫入資料的方法//把p中的內容寫入buffer,返回寫入的位元組數和錯誤資訊。如果nn<len(p),返回錯誤資訊中會包含為什麼寫入的資料比較短func (b *Writer) Write(p []byte) (nn int, err error)//將buffer中的資料寫入 io.Writerfunc (b *Writer) Flush() error//以下三個方法可以直接寫入到檔案中//寫入單個位元組func (b *Writer) WriteByte(c byte) error//寫入單個Unicode指標返回寫入位元組數錯誤資訊func (b *Writer) WriteRune(r rune) (size int, err error)//寫入字串並返回寫入位元組數和錯誤資訊func (b *Writer) WriteString(s string) (int, error)

注意:如果需要再寫入檔案時利用緩衝的話只能使用bufio包中的Write方法

樣本:

//使用bufio包中Writer對象的相關方法進行資料的寫入func WriteWithBufio(name,content string) {    if fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_APPEND,0644);err == nil {        defer fileObj.Close()        writeObj := bufio.NewWriterSize(fileObj,4096)        //       if _,err := writeObj.WriteString(content);err == nil {              fmt.Println("Successful appending buffer and flush to file with bufio's Writer obj WriteString method",content)           }        //使用Write方法,需要使用Writer對象的Flush方法將buffer中的資料刷到磁碟        buf := []byte(content)        if _,err := writeObj.Write(buf);err == nil {            fmt.Println("Successful appending to the buffer with os.OpenFile and bufio's Writer obj Write method.",content)            if  err := writeObj.Flush(); err != nil {panic(err)}            fmt.Println("Successful flush the buffer data to file ",content)        }        }}

寫檔案全部樣本

/** * @File Name: writefile.go * @Author: * @Email: * @Create Date: 2017-12-17 12:12:09 * @Last Modified: 2017-12-17 23:12:10 * @Description:使用多種方式將資料寫入檔案 */package mainimport (    "os"    "io"    "fmt"    "io/ioutil"    "bufio")func main() {      name := "testwritefile.txt"      content := "Hello, xxbandy.github.io!\n"      WriteWithIoutil(name,content)      contents := "Hello, xuxuebiao\n"      //清空一次檔案並寫入兩行contents      WriteWithFileWrite(name,contents)      WriteWithIo(name,content)      //使用bufio包需要將資料先讀到buffer中,然後在flash到磁碟中      WriteWithBufio(name,contents)}//使用ioutil.WriteFile方式寫入檔案,是將[]byte內容寫入檔案,如果content字串中沒有分行符號的話,預設就不會有分行符號func WriteWithIoutil(name,content string) {    data :=  []byte(content)    if ioutil.WriteFile(name,data,0644) == nil {        fmt.Println("寫入檔案成功:",content)        }    }//使用os.OpenFile()相關函數開啟檔案對象,並使用檔案對象的相關方法進行檔案寫入操作//清空一次檔案func WriteWithFileWrite(name,content string){    fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_TRUNC,0644)    if err != nil {        fmt.Println("Failed to open the file",err.Error())        os.Exit(2)    }    defer fileObj.Close()    if _,err := fileObj.WriteString(content);err == nil {        fmt.Println("Successful writing to the file with os.OpenFile and *File.WriteString method.",content)    }    contents := []byte(content)    if _,err := fileObj.Write(contents);err == nil {        fmt.Println("Successful writing to thr file with os.OpenFile and *File.Write method.",content)    }}//使用io.WriteString()函數進行資料的寫入func WriteWithIo(name,content string) {    fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_APPEND,0644)    if err != nil {        fmt.Println("Failed to open the file",err.Error())        os.Exit(2)    }    if  _,err := io.WriteString(fileObj,content);err == nil {        fmt.Println("Successful appending to the file with os.OpenFile and io.WriteString.",content)    }}//使用bufio包中Writer對象的相關方法進行資料的寫入func WriteWithBufio(name,content string) {    if fileObj,err := os.OpenFile(name,os.O_RDWR|os.O_CREATE|os.O_APPEND,0644);err == nil {        defer fileObj.Close()        writeObj := bufio.NewWriterSize(fileObj,4096)        //       if _,err := writeObj.WriteString(content);err == nil {              fmt.Println("Successful appending buffer and flush to file with bufio's Writer obj WriteString method",content)           }        //使用Write方法,需要使用Writer對象的Flush方法將buffer中的資料刷到磁碟        buf := []byte(content)        if _,err := writeObj.Write(buf);err == nil {            fmt.Println("Successful appending to the buffer with os.OpenFile and bufio's Writer obj Write method.",content)            if  err := writeObj.Flush(); err != nil {panic(err)}            fmt.Println("Successful flush the buffer data to file ",content)        }        }}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.