七爪源码:在 Go 中读取和写入不同的文件

读取和写入文件是程序的一个重要特性。并非所有数据都存储在与您的程序相同的内存空间中,有时您需要与其他程序共享数据或稍后使用不同的程序查看它。将数据存储在文件中是实现这些目标的好方法。今天,我们将看看如何读取和写入常用文件类型。

io.Reader, io.Writer

你有没有想过 Go 是如何读写这么多不同的东西的?这一切都归功于这些强大的接口。 io.Reader 描述了 Read 的任何内容,而 io.Writer 描述了 Write 的任何内容。因为这种行为可以很容易地复制,所以任何实现 io.Reader 和 io.Writer 接口的东西都可以用于 I/O 操作。这意味着您可以即插即用不同的输入和输出。您可以从 CSV 文件读取并将其输出到 JSON,或者您可以从标准输入读取并写入 CSV。


CSV

CSV 文件由数据行组成,其中每个值由逗号分隔(因此名称为逗号分隔值)。 CSV 不是最快的读写格式,但它用途广泛,可以被许多其他工具理解。


阅读

package mainimport (
    "encoding/csv"
    "fmt"
    "os"
)func main() {
    f, err := os.Open("see-es-vee.csv")
    if err != nil {
        fmt.Println(err)
    }
    defer f.Close()    r := csv.NewReader(f)    records, err := r.ReadAll()
    if err != nil {
        fmt.Println(err)
    }    fmt.Println(records)
}[[id fruit color taste] [0 apple red sweet] [1 banana yellow sweet] [2 lemon yellow sour] [3 grapefruit red sour]]

这是从 CSV 文件中读取数据的一种非常简单的方法。

  • 我们首先打开我们的文件 see-es-vee.csv 并将该实例保存为 f。
  • 我们想在完成后关闭 f 。 这是节省内存的好方法。 尽管这是一个简短的程序,并且关闭并不是那么必要,但养成这样做的习惯是件好事。
  • f 是 *os.File 类型,它实现了 io.Reader 接口。 因此,我们可以将 f 传递给 csv.NewReader。
  • 这将返回一个 csv.Reader 对象 r。 csv.Reader 是 io.Reader 的一种,专门读取 CSV 文件。
  • CSV 文件的每一行称为一条记录。 因此,我们可以将 CSV 文件视为记录片段。 r.ReadAll 返回此记录片。
  • 如果我们打印记录,我们可以看到行的二维切片。

这很好,但是如果我们想在读取时对每条记录应用一些操作怎么办? 幸运的是,我们可以采取更精细的方法。

func main() {
    f, err := os.Open("see-es-vee.csv")
    if err != nil {
        fmt.Println(err)
    }
    defer f.Close()    r := csv.NewReader(f)    for {
        record, err := r.Read()
        if err != io.EOF {
            break
        }
        if err != nil {
            fmt.Println(err)
        }        fmt.Println(record)
    }
}

看起来和上面的差不多吧? 直到创建 r 的部分是相同的。 让我们看看接下来会发生什么。

  • 我们进入一个无限循环,因为我们要逐行读取文件,而 Go 不知道文件有多长。
  • 我们使用 r.Read 读取每条记录。
  • 我们怎么知道我们是否到达了文件的末尾? r.Read 返回一个名为 io.EOF 的特殊错误。 EOF 代表文件结束。 我们首先捕捉到这个错误,并告诉我们的程序一旦到达终点就跳出 for 循环。
  • 对于任何其他错误,您可以按照通常的方式处理它们。
  • 处理错误后,我们可以对提取的记录做任何我们想做的事情。 我能想到的一些想法是大写、四舍五入、将其与任意值进行比较等。

现在让我们看看如何写入 CSV 文件。

package mainimport (
    "encoding/csv"
    "os"
)func main() {
    f, err := os.Create("output.csv")
    if err != nil {
        panic(err)
    }
    defer f.Close()    w := csv.NewWriter(f)    records := [][]string{
        {"id", "fruit", "color", "taste"},
        {"0", "apple", "red", "sweet"},
        {"1", "banana", "yellow", "sweet"},
        {"2", "lemon", "yellow", "sour"},
        {"3", "grapefruit", "red", "sour"},
    }    w.WriteAll(records)
}id,fruit,color,taste
0,apple,red,sweet
1,banana,yellow,sweet
2,lemon,yellow,sour
3,grapefruit,red,sour

非常简单明了。

  • 我们首先需要创建一个文件,我们可以将数据转储到该文件中。 我们将调用此 output.csv。 我们只需调用 os.Create,并将它的一个实例保存为 f。
  • 还记得我们之前是如何创建 csv.Reader 的吗? 这里我们创建了一个 csv.Writer 对象 w。 csv.NewWriter 接受 f 实现的 io.Writer 接口。
  • 我们将数据准备为二维字符串切片。 我们称之为记录。
  • 最后,我们只需使用 w.WriteAll 将记录写入 output.csv。


JSON

由于 Go 在 Web 服务中广泛使用,因此它对 JSON 具有强大的支持。 我写了一篇关于读取和写入 JSON 文件的完整文章,因此请查看该文章以获得更深入的指南!


阅读

package mainimport (
    "encoding/json"
    "fmt"
    "os"
)type fruit struct {
    Id    int    `json:"id"`
    Fruit string `json:"fruit"`
    Color string `json:"color"`
    Taste string `json:"taste"`
}func main() {
    f, err := os.Open("jay-son.json")
    if err != nil {
        fmt.Println(err)
    }
    defer f.Close()    dec := json.NewDecoder(f)    // read opening bracket
    _, err = dec.Token()
    if err != nil {
        fmt.Println(err)
    }    for dec.More() {
        var fr fruit
        err := dec.Decode(&fr)
        if err != nil {
            fmt.Println(err)
        }        fmt.Println(fr)
    }    // read closing bracket
    _, err = dec.Token()
    if err != nil {
        fmt.Println(err)
    }
}{0 apple red sweet}
{1 banana yellow sweet}
{2 lemon yellow sour}
{3 grapefruit red sour}// jay-son.json
[
    {"id": 0, "fruit": "apple", "color": "red", "taste": "sweet"},
    {"id": 1, "fruit": "banana", "color": "yellow", "taste": "sweet"},
    {"id": 2, "fruit": "lemon", "color": "yellow", "taste": "sour"},
    {"id": 3, "fruit": "grapefruit", "color": "red", "taste": "sour"}
]

这是读取 JSON 文件的简单方法。 通常,JSON 以流的形式出现。 也就是说,它们出现在对象列表中。 Go 通过解码器处理流。

我们先来看看这个:

type fruit struct {
    Id    int    `json:"id"`
    Fruit string `json:"fruit"`
    Color string `json:"color"`
    Taste string `json:"taste"`
}

该结构充当模型。因为 JSON 可以有各种形状和大小,所以理想情况下您希望拥有一个反映 JSON 结构的模型。结构字段需要是公共的,并且应该在右侧有一个标签,表示它所指的键。如果你事先不知道 JSON 会是什么样子,Go 将只使用 map[string]interface{}。

  • 首先,我们打开文件并将其实例保存到 f。不要忘记稍后再调用 close f !
  • 我们使用 json.NewDecoder 创建我们的解码器对象 dec。就像 csv.NewReader 一样,它接受一个 io.Reader。你可以开始看到接口的力量——阅读的细节被抽象出来,阅读的工作流程在这么多不同的文件类型中是一致的。
  • 一旦创建了 dec,我们就可以读取我们的 JSON。不过,只有一个问题。我们需要确保使用 dec.Token 捕捉到左括号和右括号。不这样做就像试图吃一个还没有包装的地铁三明治。呜呜。
  • 就像我们逐行读取 CSV 文件一样,我们逐个对象读取 JSON 流对象。我们循环遍历 dec.More,只要还有对象要读取,它就会返回 true。
  • 我们创建一个fruit 实例来存储我们对象的数据。使用 dec.Decode 将对象数据转储到 f。你现在可以用它做你想做的事。
  • 完成阅读后,请务必抓住右括号。


写作

写入 JSON 文件也很简单。我们称之为编码。

package mainimport (
    "encoding/json"
    "fmt"
    "os"
)type Fruit struct {
    Id    int    `json:"id"`
    Fruit string `json:"fruit"`
    Color string `json:"color"`
    Taste string `json:"taste"`
}func main() {
    f, err := os.Create("output.json")
    if err != nil {
        panic(err)
    }
    defer f.Close()    enc := json.NewEncoder(f)    apple := Fruit{Id: 0, Fruit: "apple", Color: "red", Taste: "sweet"}
    banana := Fruit{Id: 1, Fruit: "banana", Color: "yellow", Taste: "sweet"}
    lemon := Fruit{Id: 2, Fruit: "lemon", Color: "yellow", Taste: "sour"}
    grapefruit := Fruit{Id: 3, Fruit: "grapefruit", Color: "red", Taste: "sour"}    fruits := []Fruit{apple, banana, lemon, grapefruit}    err = enc.Encode(fruits)
    if err != nil {
        fmt.Println(err)
    }
}[{"id":0,"fruit":"apple","color":"red","taste":"sweet"},{"id":1,"fruit":"banana","color":"yellow","taste":"sweet"},{"id":2,"fruit":"lemon","color":"yellow","taste":"sour"},{"id":3,"fruit":"grapefruit","color":"red","taste":"sour"}]


这个也很直接。

  • 我们创建一个名为 output.json 的文件来将我们的数据转储到其中。
  • 我们使用 json.NewEncoder 创建一个新的编码器 enc。
  • 我们准备我们的数据,这是一个 Fruit 对象的切片。
  • 最后,我们使用 enc.Encode 对该切片进行编码。


Excel

Go 默认不支持读写 Excel 文件。但是,有一个名为 qax-os/excelize 的流行库可以帮助您执行此操作。拆开源码可以看到,这个包大量使用了*os.File,它也是一个io.Reader和io.Writer。我认为这显示了 io.Reader 和 io.Writer 接口的美妙之处,因为通过一些调整,您可以制作实现这些接口的自定义读取器和写入器,从而允许您支持更多文件类型。


结论

希望这篇文章能够快速介绍在 Go 中读取和写入文件,以及 io.Reader 和 io.Writer 接口的强大功能。我认为这是 Go 的优点之一 - 接口允许非常灵活和可重用的代码。当然还有更多的文件我们没有在这篇文章中介绍,但一般要点是相同的:打开文件,创建读取器或写入器,然后从中读取/写入。谢谢!


关注七爪网,获取更多APP/小程序/网站源码资源!

举报
评论 0