Потоки и файлы

Операции ввода-вывода. Reader и Writer

Последнее обновление: 15.01.2018

Язык Go имеет свою модель работы с потоками ввода-вывода, которая позволяет получать данные из различных источников - файлов, сетевых интерфейсов, объектов в памяти и т.д.

Поток данных в Go представлен байтовым срезом ([]byte), из которого можно считывать байты или в который можно заносить данные. Ключевыми типами для работы с потоками являются интерфейсы Reader и Writer из пакета io.

io.Reader

Интерфейс io.Reader предназначен для считывания данных. Он имеет следующее определение:

type Reader interface { 
	Read(p []byte) (n int, err error) 
}

Метод Read возвращает общее количество считанных байт из среза байт и информацию об ошибке, если она возникнет. Если в потоке больше нет данных, то метод должен возвращать ошибку типа io.EOF.

Рассмотрим простейший пример. Например, нам необходимо считывать номера телефонов, которые могут иметь различные форматы:

package main
import (
"fmt"
"io"
)

type phoneReader string

func (ph phoneReader) Read(p []byte) (int, error){
	count := 0
	for i := 0; i < len(ph); i++{
		if(ph[i] >= '0' && ph[i] <= '9'){
			p[count] = ph[i]
			count++
		}
	}
	return count, io.EOF
}

func main() { 
	phone1 := phoneReader("+1(234)567 9010")
	phone2 := phoneReader("+2-345-678-12-35")
	
	buffer := make([]byte, len(phone1))
	phone1.Read(buffer)
	fmt.Println(string(buffer))		// 12345679010
	
	buffer = make([]byte, len(phone2))
	phone2.Read(buffer)
	fmt.Println(string(buffer))		// 23456781235
}

Для считывания номеров телефонов определен тип phoneReader, который по сути представляет тип string. Однако phoneReader при этом реализует интерфейс Reader, то есть определяет его метод Read. В методе Read считываем данные из строки, которую представляет объект phoneReader и, если символы строки представляют числовые данные, передаем их в срез байтов. На выходе возвращаем количество считанных данных и маркер окончания чтения io.EOF. В результате при считывании из строки метод Read возвратит номер телефона, который состоит только из цифр.

При вызове метода Read создается срез байтов достаточной длины, который передается в метод Read:

buffer := make([]byte, len(phone1))
phone1.Read(buffer)

Затем с помощью инициализатора string мы можем преобразовать срез байтов в строку:

fmt.Println(string(buffer))		// 12345679010

io.Writer

Интерфейс io.Writer предназначен для записи в поток. Он определяет метод Write():

type Writer interface { 
	Write(p []byte) (n int, err error) 
}

Метод Write предназначен для копирования данных их среза байт p в определенный ресурс - файл, сетевой интерфейс и т.д. Метод возвращает количество записанных байтов и объект ошибки.

Рассмотрим примитивный пример:

package main
import "fmt"

type phoneWriter struct{ }

func (p phoneWriter) Write(bs []byte) (int, error){
	if len(bs) == 0 { 
         return 0, nil 
   }
   for i := 0; i < len(bs); i++{
		if(bs[i] >= '0' && bs[i] <= '9'){
			fmt.Print(string(bs[i]))
		}
	}
	fmt.Println()
	return len(bs), nil
}

func main() { 
	bytes1 := []byte("+1(234)567 9010")
	bytes2 := []byte("+2-345-678-12-35")
	
	writer := phoneWriter{}
	writer.Write(bytes1)
	writer.Write(bytes2)
}

Здесь структура phoneWriter реализует интерфейс Writer. В методе Write она принимает срез байтов. Предполагается, что срез байтов хранит номер телефона. Эта информация должным образом обрабатывается: из нее выделяются цифры, которые выводятся на консоль. То есть тип phoneWriter осуществляет запись потока байт на консоль.

В качестве результата метод возвращает длину среза и значение nil.

Для имитации потока байт определяются два среза байт на основе строк, которые передаются в метод Write.

На основе выше рассмотренных интерфейсов Writer и Reader основана вся система ввода-вывода в Go, и впоследствии мы более детально рассмотрим их примение при работе с файлами и сетевыми потоками.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850