【译文】go写入文件|Go主题月

1,659 阅读6分钟

译文

原文地址:golangbot.com/write-files…

在本教程中,我们将学习如何使用Go将数据写入文件。我们还将学习如何同时写入文件。

本教程包括以下部分

  • 将字符串写入文件
  • 将字节写入文件
  • 逐行将数据写入文件
  • 追加到文件
  • 同时写入文件

请在您的本地系统中运行本教程的所有程序,因为Playground不正确支持文件操作。

将字符串写入文件

最常见的文件写入操作之一是将字符串写入文件。这很容易做到。它包括以下步骤。

  1. 创建文件
  2. 将字符串写入文件

让我们立即开始编写代码。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Create("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    l, err := f.WriteString("Hello World")
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written successfully")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

在第9行的create函数将创建一个名为test.txt的文件,如果已经存在具有该名称的文件,那么create函数将截断该文件。此函数返回文件描述符。

在第14行中,我们用WriteString来写入字符串Hello World到文件中,该方法会返回写入的字节数和任何err

最后,我们用Close来关闭文件流

以上程序会输出:

11 bytes written successfully  

你可以在程序的目录下找到test.txt这个文件,可以用任何文本编辑器打开,打开后你就可以看到Hello World的文本内容。

将byte写入文件

将字节写入文件与将字符串写入文件非常相似。我们可以使用Write方法来将bytes写入文件。

以下程序将字节切片写入文件。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Create("/home/naveen/bytes")
    if err != nil {
        fmt.Println(err)
        return
    }
    d2 := []byte{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100}
    n2, err := f.Write(d2)
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(n2, "bytes written successfully")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

在上面程序的第15行中,我们使用Write方法将bytes切片写入/home/naveen文件夹下名为bytes的文件里。当然你也可以更换其它任意文件夹。剩下的程序就比较简单了。该程序会创建bytes文件并且输出11 bytes written successfully。打开文件后你可以看到hello bytes文本内容。

逐行将数据写入文件

另一个常见的文件操作是需要逐行将字符串写入文件。在本节中,我们将编写一个程序来创建具有以下内容的文件。

Welcome to the world of Go.  
Go is a compiled language.  
It is easy to learn Go.  

让我们立即开始编写代码。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Create("lines")
    if err != nil {
        fmt.Println(err)
                f.Close()
        return
    }
    d := []string{"Welcome to the world of Go1.", "Go is a compiled language.", "It is easy to learn Go."}

    for _, v := range d {
        fmt.Fprintln(f, v)
        if err != nil {
            fmt.Println(err)
            return
        }
    }
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("file written successfully")
}

在第9行程序中,我们创建名为lines的新文件。在第17行中我们使用for range循环遍历数组,并使用Fprintln函数将行写入文件。Fprintln函数将io.writer作为参数,并添加新行,正是我们想要的。运行该程序将打印成功写入的文件,并且将在当前目录中创建文件行。文件行的内容在下面提供。

Welcome to the world of Go1.  
Go is a compiled language.  
It is easy to learn Go. 

追加到文件

在本节中,我们将在上一节中创建的lines文件中追加一行。我们将在lines文件末尾追加一行File handling is easy

该文件必须以追加和仅写入模式打开。这些标志被传递,参数被传递给Open函数。在追加模式下打开文件后,我们将新行添加到文件中。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.OpenFile("lines", os.O_APPEND|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println(err)
        return
    }
    newLine := "File handling is easy."
    _, err = fmt.Fprintln(f, newLine)
    if err != nil {
        fmt.Println(err)
                f.Close()
        return
    }
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("file appended successfully")
}

在第9行程序中,我们用追加和仅写入模式打开文件。在文件成功打开后,我们在程序的第15行向文件添加新一行。程序将会输出file appended successfully。运行该程序后,lines文件的内容将为:

Welcome to the world of Go1.  
Go is a compiled language.  
It is easy to learn Go.  
File handling is easy. 

同时写入文件

当多个goroutine并发写入文件时,我们将以竞争状态结束。因此,应使用通道来协调对文件的并发写入。

我们将编写一个创建100个goroutine的程序。每个goroutine将同时生成一个随机数,因此总共生成了一百个随机数。这些随机数将被写入文件。我们将通过以下方法解决此问题。

  1. 创建一个通道,该通道将用于读取和写入生成的随机数。
  2. 创建100个生产者goroutine。每个goroutine将生成一个随机数,并将该随机数写入通道。
  3. 创建一个消费者goroutine,它将从通道读取并将生成的随机数写入文件。因此,我们只有一个goroutine同时写入文件,从而避免了竞争情况:)
  4. 完成后关闭文件。

让我们首先编写produce函数,该函数生成随机数。

func produce(data chan int, wg *sync.WaitGroup) {  
    n := rand.Intn(999)
    data <- n
    wg.Done()
}

上面的函数生成一个随机数并将其写入通道数据,然后在等待组上调用Done来通知它已完成其任务。

让我们转到现在写入文件的函数。

func consume(data chan int, done chan bool) {  
    f, err := os.Create("concurrent")
    if err != nil {
        fmt.Println(err)
        return
    }
    for d := range data {
        _, err = fmt.Fprintln(f, d)
        if err != nil {
            fmt.Println(err)
            f.Close()
            done <- false
            return
        }
    }
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        done <- false
        return
    }
    done <- true
}

consume函数将创建一个名为concurrent的文件。然后,它从数据通道读取随机数并写入文件。一旦读取并写入了所有随机数,就将true写入完成的通道,以通知它已完成其任务。

让我们编写main函数完成此程序。我在下面提供了整个程序。

package main

import (  
    "fmt"
    "math/rand"
    "os"
    "sync"
)

func produce(data chan int, wg *sync.WaitGroup) {  
    n := rand.Intn(999)
    data <- n
    wg.Done()
}

func consume(data chan int, done chan bool) {  
    f, err := os.Create("concurrent")
    if err != nil {
        fmt.Println(err)
        return
    }
    for d := range data {
        _, err = fmt.Fprintln(f, d)
        if err != nil {
            fmt.Println(err)
            f.Close()
            done <- false
            return
        }
    }
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        done <- false
        return
    }
    done <- true
}

func main() {  
    data := make(chan int)
    done := make(chan bool)
    wg := sync.WaitGroup{}
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go produce(data, &wg)
    }
    go consume(data, done)
    go func() {
        wg.Wait()
        close(data)
    }()
    d := <-done
    if d == true {
        fmt.Println("File written successfully")
    } else {
        fmt.Println("File writing failed")
    }
}

主函数创建一个data通道,从该通道读取和写入随机数。done的通道消费goroutine通知main它已完成其任务。wg的waitgroup用于等待所有100个goroutine完成生成随机数。

for循环创建100和goroutines。goroutine在等待组上调用wait()以等待所有100个goroutine完成创建随机数。之后,它关闭通道。一旦关闭通道,并且消费goroutine已将所有生成的随机数写入文件,它将true写入done的通道。主goroutine解除阻塞并打印File written successfully

现在,您可以在任何文本编辑器中同时打开文件,并查看100个生成的随机数:)

这使我们结束了本教程。希望您喜欢阅读。祝你有美好的一天。