七爪源码:在 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/小程序/网站源码资源!
请先 后发表评论~