如何使用Go读/写文件?

Set*_*nig 274 file go

我一直在努力学习Go,但我一直难以尝试从普通文件读取和写入.

我可以得到inFile, _ := os.Open(INFILE, 0, 0),但实际上获取文件的内容没有意义,因为read函数将a []byte作为参数.

func (file *File) Read(b []byte) (n int, err Error)
Run Code Online (Sandbox Code Playgroud)

Mos*_*afa 460

让我们创建一个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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

bufio这里只是作为一个缓冲区,因为我们与数据没什么关系.在大多数其他情况下(特别是文本文件)bufio非常有用,它为我们提供了一个很好的API,可以轻松灵活地进行读写,同时它可以处理幕后缓冲.

运用 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

非常简单!但只有在你确定自己没有处理大文件时才使用它.

  • 对于任何偶然发现这个问题的人来说,最初是在2009年引入这些库之前询问的,所以请将此答案作为指导! (53认同)
  • 请注意,这些示例不检查fo.Close()的错误返回.从Linux手册页关闭(2):不检查close()的返回值是一个常见但严重的编程错误.很可能首先在最后的close()上报告先前write(2)操作的错误.关闭文件时不检查返回值可能会导致数据无声丢失.使用NFS和磁盘配额尤其可以观察到这种情况. (14认同)
  • 那么,什么是"大"文件?1KB?1MB?1GB?或者"大"取决于机器的硬件? (12认同)
  • 根据http://golang.org/pkg/os/#File.Write,当 Write 尚未写入所有字节时,它会返回错误。因此第一个示例中的额外检查(`panic("error inwriting")`)是不必要的。 (2认同)
  • @ 425nesp它将整个文件读入内存,因此它取决于正在运行的机器中的可用内存量. (2认同)

小智 48

这是一个很好的版本:

package main

import (
  "io/ioutil"; 
  )


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

  • 另外,`0x777`是假的.在任何情况下,它应该更像是'0644`或'0755`(八进制,而不是十六进制). (9认同)
  • 这会将整个文件存储在内存中.由于文件可能很大,这可能并不总是您想要做的. (8认同)

use*_*610 28

运用 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)
}
Run Code Online (Sandbox Code Playgroud)

如果你不喜欢重新发明轮子,该io.Copyio.CopyN可以为你服务好.如果你检查 io.Copy函数的来源,它只不过是一个封装在Go库中的Mostafa解决方案之一(实际上是"基本的"解决方案).不过,他们使用的缓冲区比他大得多.

  • 有一点值得一提 - 为了确保文件的内容写入磁盘,你需要在`io.Copy(w,r)之后使用`w.Sync()` (5认同)
  • @Invidian 这完全取决于您打开目标文件的方式。如果您执行 `w, err := os.Create("output.txt")`,则您所描述的情况不会发生,因为“Create 创建或截断指定文件。如果该文件已存在,则会被截断。” https://golang.org/pkg/os/#Create。 (2认同)

pet*_*rSO 10

[]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();
}
Run Code Online (Sandbox Code Playgroud)

  • @peterSO:使用defer (10认同)
  • Go约定首先检查错误,让普通代码驻留在`if`块之外 (9认同)

Sal*_*ali 8

使用较新的Go版本,可以轻松读取/写入文件.要从文件中读取:

package main

import (
    "fmt"
    "io/ioutil"
)

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

要写入文件:

package main

import "os"

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

    file.WriteString("test\nhello")
}
Run Code Online (Sandbox Code Playgroud)

这将覆盖文件的内容(如果不存在则创建新文件).


mar*_*ter 7

试试这个:

package main

import (
  "io"; 
  )


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

  • 现在已将其移至ioutil包中.所以它将是ioutil.ReadFile() (7认同)
  • 你应该真的检查错误代码,而不是忽略它! (3认同)
  • 如果您想一次读取整个文件,这将起作用。如果文件非常大或者您只想读取其中的一部分,那么它可能不是您要查找的内容。 (2认同)
  • "0x644"是否正确?那和'0644`不一样. (2认同)

Ina*_*mus 7

新方法

从 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)
    }

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

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


小智 5

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)
}
Run Code Online (Sandbox Code Playgroud)