320

我一直在尝试自学 Go,但在尝试读取和写入普通文件时遇到了困难。

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

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

10 回答 10

534

让我们列出在 Go 中读取和写入文件的所有方式的 Go 1 兼容列表。

因为文件 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在这里只是充当缓冲区,因为我们与数据没有太多关系。在大多数其他情况下(特别是文本文件) ,它为我们提供了一个很好的 API,可以轻松灵活地进行读写,同时它在后台处理缓冲,这bufio非常有用。


注意:以下代码适用于较旧的 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)
    }
}

非常简单!但只有在您确定不处理大文件时才使用它。

于 2012-03-16T15:17:18.610 回答
54

这是一个很好的版本:

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}
于 2010-04-11T18:10:38.173 回答
34

使用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.Copyandio.CopyN可能会很好地为您服务。如果您检查io.Copy 函数的来源,它只不过是封装在 Go 库中的 Mostafa 解决方案之一(实际上是“基本”解决方案)。不过,他们使用的缓冲区比他大得多。

于 2012-10-23T20:46:16.620 回答
18

使用较新的 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")
}

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

于 2015-11-23T03:38:44.827 回答
13

新的方法

从 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)
    }
}
于 2021-01-30T16:38:06.387 回答
10

[]byte is a slice (similar to a substring) of all or part of a byte array. Think of the slice as a value structure with a hidden pointer field for the system to locate and access all or part of an array (the slice), plus fields for the length and capacity of the slice, which you can access using the len() and cap() functions.

Here's a working starter kit for you, which reads and prints a binary file; you will need to change the inName literal value to refer to a small file on your system.

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();
}
于 2009-11-30T23:44:07.760 回答
7

尝试这个:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}
于 2009-11-30T20:14:40.227 回答
3

你也可以使用这个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)
}
于 2021-08-15T14:54:32.093 回答
1

只需查看文档,您似乎应该只声明一个 []byte 类型的缓冲区并将其传递给 read,然后它将读取那么多字符并返回实际读取的字符数(以及一个错误)。

文档

Read 从文件中读取最多 len(b) 个字节。它返回读取的字节数和错误(如果有)。EOF 由 err 设置为 EOF 的零计数发出信号。

那不行吗?

编辑:另外,我认为您可能应该使用在bufio包中声明的 Reader/Writer 接口,而不是使用os包。

于 2009-11-30T19:22:41.883 回答
1

Read 方法接受一个字节参数,因为这是它将读取到的缓冲区。在某些圈子中,这是一个常见的成语,当你考虑它时,它是有道理的。

通过这种方式,您可以确定读取器将读取多少字节并检查返回以查看实际读取了多少字节并适当地处理任何错误。

正如其他人在他们的答案中指出的那样, bufio 可能是您想要从大多数文件中读取的内容。

我将添加另一个提示,因为它真的很有用。从文件中读取一行最好不要使用 ReadLine 方法,而是使用 ReadBytes 或 ReadString 方法。

于 2012-03-19T03:24:54.843 回答