Чтение и запись файлов — важная функция вашей программы. Не все данные хранятся в том же пространстве памяти, что и ваша программа, и иногда вам нужно будет поделиться данными с другими программами или просмотреть их позже в другой программе. Хранение ваших данных в файле — хороший способ достичь этих целей. Сегодня мы рассмотрим, как вы можете читать и писать в часто используемые типы файлов.

io.Чтение, io.Писатель

Вы когда-нибудь задумывались, как Go может читать и писать так много разных вещей? Это все благодаря этим мощным интерфейсам. io.Reader описывает все, что относится к Read, а io.Writer описывает все, что относится к Write. Так как это поведение можно легко воспроизвести, все, что реализует интерфейсы io.Reader и io.Writer, можно использовать для операций ввода-вывода. Это означает, что вы можете подключать и воспроизводить различные входы и выходы. Вы можете прочитать из CSV-файла и вывести его в JSON, или вы можете прочитать из stdin и записать в CSV.

CSV

Файл CSV состоит из строк данных, где каждое значение разделено запятой (отсюда и название значений, разделенных запятыми). CSV — не самый быстрый формат для чтения и записи, но он очень универсален и может быть понят многими другими инструментами.

Чтение

package main
import (
    "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 возвращает этот фрагмент записей.
  • Если мы напечатаем records, мы увидим двумерный срез строк.

Это здорово, но что, если мы хотим применять какие-то операции к каждой записи по мере чтения? К счастью, мы можем использовать более детальный подход.

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 такая же. Посмотрим, что будет дальше.

  • Мы вводим бесконечный цикл for, потому что хотим читать файл построчно, а Go не знает, какой длины файл.
  • Мы читаем каждую запись, используя r.Read.
  • Как мы узнаем, достигли ли мы конца файла? r.Read возвращает специальную ошибку под названием io.EOF. EOF означает конец файла. Сначала мы перехватываем эту ошибку и сообщаем нашей программе выйти из цикла for, как только мы достигнем конца.
  • Любые другие ошибки можно обработать обычным образом.
  • После обработки ошибок мы можем делать с извлеченной записью все, что захотим. Некоторые идеи, которые я могу придумать, - это капитализация, округление, сравнение с произвольным значением и т. Д.

Теперь давайте посмотрим, как записать файл CSV.

package main
import (
    "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 принимает интерфейс io.Writer, который реализует f.
  • Мы подготавливаем наши данные в виде двумерного среза строк. Мы назовем это records.
  • Наконец, мы просто используем w.WriteAll для записи records в output.csv.

JSON

Поскольку Go широко используется в веб-сервисах, он имеет надежную поддержку JSON. Я написал целую статью о чтении и записи файлов JSON, поэтому ознакомьтесь с более подробным руководством!

Чтение

package main
import (
    "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. Не забудьте отложить вызов, чтобы закрыть f позже!
  • Мы создаем наш объект декодера dec, используя json.NewDecoder. Как и csv.NewReader, он принимает io.Reader. Вы можете начать видеть мощь интерфейсов — детали чтения абстрагируются, а рабочий процесс чтения един для множества различных типов файлов.
  • После создания dec мы можем прочитать наш JSON. Но только одна проблема. Нам нужно убедиться, что мы ловим открывающие и закрывающие скобки с помощью dec.Token. Не делать этого — все равно, что пытаться съесть бутерброд в метро, ​​не закрыв обертку. Бле.
  • Точно так же, как мы читаем файл CSV построчно, мы читаем объект потока JSON за объектом. Мы перебираем dec.More, который возвращает true до тех пор, пока остаются объекты для чтения.
  • Мы создаем экземпляр fruit для хранения данных нашего объекта. Используйте dec.Decode для сброса данных объекта в f. Вы можете делать с этим что хотите сейчас.
  • После того, как вы закончите чтение, не забудьте поймать закрывающую скобку.

Письмо

Запись в файл JSON также проста. Мы называем это кодированием.

package main
import (
    "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 для сброса наших данных.
  • Мы создаем новый кодировщик enc, используя json.NewEncoder.
  • Мы подготавливаем наши данные, которые представляют собой срез 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 — интерфейсы позволяют создавать очень гибкий и повторно используемый код. Конечно, есть еще файлы, которые мы не рассмотрели в этом посте, но общий смысл тот же: откройте файл, создайте программу чтения или записи и читайте/пишите из него. Спасибо!

Вы также можете прочитать этот пост на Dev.to и моем личном сайте.