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