36、Golang 教程 - 写入文件

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

本教程包含以下部分

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

将字符串写入文件是最常见的文件写入操作之一,这很简单。它包括以下步骤。

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写入文件。此方法返回写入的字节数错误(如果有)。

最后,我们在第21行关闭文件。

运行程序,输出:

11 bytes written successfully  

你可以在执行此程序的目录中找到名为test.txt的文件。使用任何文本编辑器打开该文件,你会发现其中包含文本Hello World

将字节写入文件

将字节写入文件与将字符串写入文件非常相似。我们将使用Write方法将字节写入文件。下面的程序将一段字节写入文件。

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方法将一段字节写入目录/home/naveen中名为bytes文件中。你可以将此目录更改为其他目录。其他代码很简单。此程序将打印11 bytes written successfully,并将创建一个名为bytes的文件。打开该文件,你可以看到它包含文本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循环迭代数组,并使用Fprintln函数将这些行写入文件。Fprintln函数接受io.write作为参数,并附加一个新行,这正是我们想要的。运行此程序将打印file written successfully,并在当前目录中创建lines文件。下面是lines文件中的内容。

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个goroutines。每个goroutine将同时生成一个随机数,从而总共生成一百个随机数。这些随机数将写入文件。我们将使用以下方法解决此问题。

1、 创建一个用于读取和写入随机数的信道;
2、 创建100个生产者goroutines协程每个goroutine协程将生成一个随机数,并将随机数写入信道;
3、 创建一个消费者goroutine协程,它将从信道读取随机数并写入文件因此,我们只有一个goroutine同时写入文件,从而避免竞争条件;
4、 完成后关闭文件;

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

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

上面的函数生成一个随机数,并将其写入data信道,然后调用waitgroup 的 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 的文件,然后,它从data信道读取随机数并写入文件。一旦读写完所有的随机数,它就会向done信道写入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")
    }
}

在第41main函数中创建了读取和写入随机数的data信道。第42行创建 consume协程用来通知main函数它已经完成任务的done信道。第43行的wg waitgroup用来阻塞100个协程完成随机数的写入。

44行中的for循环创建了100goroutine协程。第49行的goroutine调用waitgroupwait(),以等待100goroutine完成随机数的创建。之后,它关闭了data信道。一旦信道关闭,并且consume协程已经完成将所有随机数写入到文件中,在第37行,它就会done信道写入true,然后main协程将被解除阻塞,并成功打印 File written successfully

现在,你可以在任何文本编辑器中打开该文件,并查看生成的100个随机数字。