美文网首页
Go教程第十四篇:文件写入

Go教程第十四篇:文件写入

作者: 大风过岗 | 来源:发表于2020-04-18 11:28 被阅读0次

写文件

在这篇教程中,我们将学到使用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
    }
}

上面程序中的create()函数,创建了一个名为test.txt的文件。如果这个名字的文件已经存在了,那么此函数就会清空此文件。create()函数会返回一个文件描述符。

l, err := f.WriteString("Hello World") 在这行代码中,我们使用WriteString方法把字符串“Hello World”写入到文件中。该方法会返回写入的字节数以及出现的错误。
err = f.Close()在这里我们关闭了文件。程序的输出如下:

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

n2, err := f.Write(d2)在这行代码中,我们使用Write方法把一个切片的字节写入到名为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")
}

f, err := os.Create("lines")这行代码创建了一个名为lines的文件。for _, v := range d 这样代码,我们使用for循环来遍历一个数组,并使用Fprintln函数把这些行写入到文件中。Fprintln函数接收了一个Writer作为参数,并把一行字符串追加到文件中。和我们预期的一样,运行这个程序的话,会打印出file written successfully。同时在目录下会新建一个名为lines的文件。lines文件的内容如下:

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

向文件中追加内容

在这部分,我们会向lines文件追加一行内容。我们会把"File handling is easy"追加到lines文件中。
在这种模式下,文件必须处在Append and Write only mode模式下。这些标志会作为参数传递给Open函数。
文件以append模式打开之后,我们就可以向文件中添加一行代码。

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

在上面的程序中,我们在append and write only mode模式下,打开文件。成功打开文件之后,我们向文件中写入一行数据。
程序输出"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并发地向一个文件写入数据时,会发生资源竞争。因此,并发写入同一个文件,需要使用channel进行协调。

我们会写一个程序,这个程序会创建100个goroutine。每个goroutine都将产生一个并发的随机数,所以总共会产生100个随机数。
这些随机数会被写入到文件中,我们将使用到下面这些方法来解决这个问题。

  1. 创建一个channel,我们将使用这个channel来把生成的随机数写入到文件中。

  2. 创建100个生产者goroutine,其中每一个goroutine都将产生了一个随机数,并把这个随机数写入到channel中。

  3. 创建一个消费者goroutine,这个goroutine会从channel中读取,并把生成的随机数写入到文件中。因此,我们只有一个goroutine向文件中写入随机数。
    因此,也就避免了资源竞争的问题。

  4. 完成之后,关闭文件

我们先来写一个produce函数,由这个produce函数生成随机数。

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

上面的这个函数会生成一个随机数,并把随机数写入到通道data中,之后调用了waitgroup的Done()方法,用以提醒waitgroup,此项任务
已完成。

我们再来看一下,把随机数写入到文件的函数:

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通道中读取随机数,同时把随机数写入到文件中。
一旦它完成所有随机数的读写,它就把true写入到done通道中,用以完成执行完成的通知。

我们来写一个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")
    }
}

main函数创建了一个data通道,程序会从data通道中读取随机数,然后写入到文件中。
当任务完成之后,消费者goroutine会使用done通道来通知main函数。wg等待组会等待所有的100个goroutine完成随机数的生成。

在第44行的for循环中,我们创建了100个goroutine。我们在waitgroup中调用wait方法,等待所有的100个goroutine都已完成随机数的生成。完成之后,就把通道关闭。当通道关闭的时候,消费者goroutine已经把所有生成的随机数都写入到了文件中。
它把true写入到done通道中,main函数的goroutine收到通知之后,取消阻塞,并打印出: File written successfully。

此时,你打开concurrent文件,就会看到里面有100个随机数。

致谢

感谢您百忙之中阅读本文。如果有任何反馈和问题,请您在评论区留言。

备注

本文系翻译之作原文博客地址

相关文章

  • Go教程第十四篇:文件写入

    写文件 在这篇教程中,我们将学到使用Go向文件中写入数据。我们还将学到如何并发写入一个文件。 文章包含以下几个部分...

  • Golang学习笔记-1.14 Maps

    本文系第十四篇Golang语言学习教程 What is maps? map是在 go 中将键(key) 与值(va...

  • pandas数据分析中常用方法

    官方教程 读取写入文件 官方IO 读取 写入 read_csv to_csv read_excel to_exce...

  • Go打包

    1、Windows版的打包创建一个文件 go_build.txt写入内容 将go_build.txt更名为go_b...

  • Go语言(3)IO操作

    Go语言(3)IO操作 创建文件夹 删除文件夹 检查文件是否存在 创建文件 文件写入(io.WriteString...

  • Goland 设置模版注释

    按图操作,非常简单 # 这个head 是文件 一会要用到 package需要自己写入 选择Go File 文件 ,...

  • Go教程第五篇:常量

    Go教程第五篇:常量 本文是《Go系列教程》的第五篇 定义 Go里面的常量术语是用于表示固定值,例如: 5,-89...

  • Go语言基础

    使用Go编写第一行代码 新建golangFile文件夹,在文件夹中新建main.go,在里面写入如下代码 编译 g...

  • 记一次go run main.go kill 问题

    问题描述: 写了个脚本在执行,如下: 向一个文件写入数据。用go run main.go & 此时我们ps -ef...

  • Go教程第十九篇: Channels

    Go教程第十九篇: Channels 本文是《Go系列教程》的第十九篇文章。 在上一篇文章中,我们讨论了并发是如何...

网友评论

      本文标题:Go教程第十四篇:文件写入

      本文链接:https://www.haomeiwen.com/subject/rhhvvhtx.html