簡體   English   中英

如何使用 Go 從/向文件讀/寫

[英]How to read/write from/to a file using Go

我一直在嘗試自己學習 Go,但我一直在嘗試讀取和寫入普通文件。

我可以得到inFile, _ := os.Open(INFILE, 0, 0) ,但實際上獲取文件的內容沒有意義,因為 read 函數需要一個[]byte作為參數。

func (file *File) Read(b []byte) (n int, err Error)

讓我們制作一個 Go 1 兼容的列表,列出在 Go 中讀寫文件的所有方式。

因為文件 API 最近發生了變化,並且大多數其他答案不適用於 Go 1。他們也錯過了bufio ,恕我直言,這很重要。

在以下示例中,我通過讀取文件並寫入目標文件來復制文件。

從基礎開始

package main

import (
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := fi.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := fo.Write(buf[:n]); err != nil {
            panic(err)
        }
    }
}

在這里,我用os.Openos.Create其周圍便利的包裝os.OpenFile 我們通常不需要直接調用OpenFile

注意處理EOF。 Read嘗試在每次調用時填充buf ,並在到達文件末尾時返回io.EOF作為錯誤。 在這種情況下, buf仍將保存數據。 隨后對Read調用返回零作為讀取的字節數和相同的io.EOF作為錯誤。 任何其他錯誤都會導致恐慌。

使用bufio

package main

import (
    "bufio"
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()
    // make a read buffer
    r := bufio.NewReader(fi)

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()
    // make a write buffer
    w := bufio.NewWriter(fo)

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := r.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := w.Write(buf[:n]); err != nil {
            panic(err)
        }
    }

    if err = w.Flush(); err != nil {
        panic(err)
    }
}

bufio在這里只是充當緩沖區,因為我們與數據沒有太大關系。 在大多數其他情況下(特別是文本文件), bufio非常有用,它為我們提供了一個很好的 API ,可以輕松靈活地讀寫,同時它在后台處理緩沖。


注意:以下代碼適用於較舊的 Go 版本(Go 1.15 及之前)。 事情變了。 對於新的方式,看看這個答案

使用ioutil

package main

import (
    "io/ioutil"
)

func main() {
    // read the whole file at once
    b, err := ioutil.ReadFile("input.txt")
    if err != nil {
        panic(err)
    }

    // write the whole body at once
    err = ioutil.WriteFile("output.txt", b, 0644)
    if err != nil {
        panic(err)
    }
}

非常簡單! 但是只有在您確定您沒有處理大文件時才使用它。

這是一個很好的版本:

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}

使用io.Copy

package main

import (
    "io"
    "log"
    "os"
)

func main () {
    // open files r and w
    r, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    defer r.Close()

    w, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    defer w.Close()

    // do the actual work
    n, err := io.Copy(w, r)
    if err != nil {
        panic(err)
    }
    log.Printf("Copied %v bytes\n", n)
}

如果您不想重新發明輪子, io.Copyio.CopyN可能會很好地為您服務。 如果您查看io.Copy 函數的源代碼,它只不過是打包在 Go 庫中的 Mostafa 解決方案之一(實際上是“基本”解決方案)。 不過,他們使用的緩沖區比他大得多。

使用較新的 Go 版本,讀取/寫入文件很容易。 從文件中讀取:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("text.txt")
    if err != nil {
        return
    }
    fmt.Println(string(data))
}

要寫入文件:

package main

import "os"

func main() {
    file, err := os.Create("text.txt")
    if err != nil {
        return
    }
    defer file.Close()

    file.WriteString("test\nhello")
}

這將覆蓋文件的內容(如果不存在則創建一個新文件)。

[]byte是字節數組的全部或部分的切片(類似於子字符串)。 將切片視為具有隱藏指針字段的值結構,供系統定位和訪問數組的全部或部分(切片),以及切片長度和容量的字段,您可以使用len()cap()函數。

這是一個適合您的入門工具包,它可以讀取和打印二進制文件; 您需要更改inName文字值以引用系統上的一個小文件。

package main
import (
    "fmt";
    "os";
)
func main()
{
    inName := "file-rw.bin";
    inPerm :=  0666;
    inFile, inErr := os.Open(inName, os.O_RDONLY, inPerm);
    if inErr == nil {
        inBufLen := 16;
        inBuf := make([]byte, inBufLen);
        n, inErr := inFile.Read(inBuf);
        for inErr == nil {
            fmt.Println(n, inBuf[0:n]);
            n, inErr = inFile.Read(inBuf);
        }
    }
    inErr = inFile.Close();
}

新方法

從 Go 1.16 開始,使用os.ReadFile將文件加載到內存,並使用os.WriteFile從內存寫入文件( ioutil.ReadFile現在調用os.ReadFile )。

小心os.ReadFile因為它會將整個文件讀入內存。

package main

import "os"

func main() {
    b, err := os.ReadFile("input.txt")
    if err != nil {
        log.Fatal(err)
    }

    // `b` contains everything your file does
    // This writes it to the Standard Out
    os.Stdout.Write(b)

    // You can also write it to a file as a whole
    err = os.WriteFile("destination.txt", b, 0644)
    if err != nil {
        log.Fatal(err)
    }
}

嘗試這個:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}

僅查看文檔,您似乎應該聲明一個 []byte 類型的緩沖區並將其傳遞給 read ,然后讀取該多個字符並返回實際讀取的字符數(和錯誤)。

文檔

Read 從文件中讀取最多 len(b) 個字節。 它返回讀取的字節數和錯誤(如果有)。 EOF 由 err 設置為 EOF 的零計數表示。

那不行嗎?

編輯:另外,我認為您或許應該使用bufio包中聲明的 Reader/Writer 接口,而不是使用os包。

Read 方法接受一個字節參數,因為這是它將讀入的緩沖區。 這是某些圈子中的常見習語,當您考慮它時會有些道理。

通過這種方式,您可以確定讀取器將讀取多少字節並檢查返回以查看實際讀取了多少字節並適當地處理任何錯誤。

正如其他人在他們的答案中指出的那樣, bufio 可能是您從大多數文件中讀取的內容。

我會添加另一個提示,因為它真的很有用。 從文件中讀取一行最好不要通過 ReadLine 方法完成,而是通過 ReadBytes 或 ReadString 方法完成。

您也可以使用fmt包:

package main

import "fmt"

func main(){
    file, err := os.Create("demo.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    
    fmt.Fprint(file, name)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM