Fluxuri de date și fișiere
Operații de intrare-ieșire / Reader și Writer
Limbajul Go are propriul său model de lucru cu fluxuri de intrare-ieșire, care permite obținerea de date din diverse surse - fișiere, interfețe de rețea, obiecte în memorie etc.
Fluxul de date în Go este reprezentat de un slice de octeți ([]byte), din care se pot citi octeți sau în care se pot adăuga date. Tipurile cheie pentru lucrul cu fluxuri sunt interfețele Reader și Writer din pachetul io.
io.Reader
Interfața io.Reader este destinată citirii datelor. Ea are următoarea definiție:
type Reader interface {
Read(p []byte) (n int, err error)
}
Metoda Read returnează numărul total de octeți citiți din slice-ul de octeți și informațiile despre eroare, dacă aceasta apare. Dacă nu mai sunt date în flux, metoda trebuie să returneze eroarea de tip io.EOF.
Să luăm un exemplu simplu. De exemplu, trebuie să citim numere de telefon care pot avea formate diferite:
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
}
Pentru citirea numerelor de telefon este definit tipul phoneReader, care de fapt reprezintă tipul string. Totuși, phoneReader implementează interfața Reader, adică definește metoda sa Read. În metoda Read, citim date din șirul reprezentat de obiectul phoneReader și, dacă caracterele din șir sunt date numerice, le transmitem într-un slice de octeți. La ieșire returnăm numărul de date citite și marker-ul de sfârșit al citirii io.EOF. Ca rezultat, la citirea din șir, metoda Read va returna numărul de telefon care conține doar cifre.
La apelul metodei Read se creează un slice de octeți de dimensiunea necesară, care este transmis metodei Read:
buffer := make([]byte, len(phone1))
phone1.Read(buffer)
Apoi, folosind inițializatorul string, putem transforma slice-ul de octeți într-un șir de caractere:
fmt.Println(string(buffer)) // 12345679010
io.Writer
Interfața io.Writer este destinată scrierii în flux. Ea definește metoda Write():
type Writer interface {
Write(p []byte) (n int, err error)
}
Metoda Write este destinată copierii datelor din slice-ul de octeți p într-o resursă specifică - fișier, interfață de rețea etc. Metoda returnează numărul de octeți scriși și obiectul de eroare.
Să luăm un exemplu simplu:
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)
}
Aici, structura phoneWriter implementează interfața Writer. În metoda Write, aceasta primește un slice de octeți. Se presupune că acest slice de octeți conține un număr de telefon. Informațiile sunt procesate corespunzător: se extrag cifrele și sunt afișate pe consolă. Așadar, tipul phoneWriter realizează scrierea fluxului de octeți pe consolă.
Ca rezultat, metoda returnează lungimea slice-ului și valoarea nil.
Pentru a simula fluxul de octeți, sunt definite două slice-uri de octeți pe baza șirurilor, care sunt transmise metodei Write.
Pe baza interfețelor Writer și Reader discutate mai sus, se bazează întregul sistem de intrare-ieșire în Go, iar ulterior vom examina mai detaliat utilizarea acestora în lucrul cu fișiere și fluxuri de rețea.