Menu
Coddy logo textTech

io.Reader & io.Writer

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 73 sur 107.

La bibliothèque standard de Go illustre la conception basée sur les interfaces à travers deux interfaces fondamentales : io.Reader et io.Writer. Ces interfaces simples alimentent tout, des opérations sur les fichiers à la communication réseau.

L'interface io.Reader nécessite une seule méthode :

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

Il lit jusqu'à len(p) octets dans le slice et renvoie le nombre d'octets lus. De même, io.Writer définit :

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

Tout type implémentant ces méthodes fonctionne avec l'ensemble de l'écosystème I/O.

Voici un type personnalisé qui implémente io.Reader :

type RepeatReader struct {
    Char  byte
    Count int
    pos   int
}

func (r *RepeatReader) Read(p []byte) (int, error) {
    if r.pos >= r.Count {
        return 0, io.EOF
    }
    n := 0
    for n < len(p) && r.pos < r.Count {
        p[n] = r.Char
        n++
        r.pos++
    }
    return n, nil
}

Désormais, ce lecteur personnalisé fonctionne avec n'importe quelle fonction attendant un io.Reader :

reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data))  // AAAAA

Cette conception vous permet d'écrire des fonctions qui acceptent io.Reader ou io.Writer, ce qui leur permet de fonctionner avec des fichiers, des connexions réseau, des tampons ou toute implémentation personnalisée.

challenge icon

Défi

Facile

Construisons un processeur de flux de texte personnalisé qui implémente les interfaces io.Reader et io.Writer ! Vous allez créer des types capables de transformer le texte au fur et à mesure qu'il les traverse, démontrant ainsi comment ces interfaces fondamentales permettent une composition d'E/S puissante.

Vous organiserez votre code sur deux fichiers :

  • streams.go : Définissez vos types de lecteur et d'écrivain personnalisés.

    Créez une structure CountingReader qui enveloppe une chaîne de caractères et suit le nombre d'octets lus. Elle doit comporter des champs pour la chaîne source et un suivi de position. Implémentez la méthode Read(p []byte) (int, error) qui lit les octets de la source dans la tranche (slice) fournie, met à jour la position et renvoie io.EOF lorsque la source est épuisée.

    Créez une structure UppercaseWriter qui collecte les données écrites et les convertit en majuscules. Elle doit stocker le résultat accumulé en interne. Implémentez la méthode Write(p []byte) (int, error) qui convertit les octets entrants en majuscules et les stocke. Ajoutez une méthode Result() string pour récupérer le texte en majuscules accumulé.

    Créez des fonctions constructeurs NewCountingReader(source string) *CountingReader et NewUppercaseWriter() *UppercaseWriter pour initialiser vos types correctement.

  • main.go : Démontrez le fonctionnement de vos types d'E/S personnalisés avec la bibliothèque standard.

    Lisez une chaîne de caractères en entrée, puis utilisez votre CountingReader avec io.ReadAll pour lire toutes les données. Passez ces données à votre UppercaseWriter en utilisant sa méthode Write.

    Affichez les résultats dans ce format :

    Original: [input]
    Uppercase: [result from writer]
    Bytes read: [total bytes]

L'entrée suivante sera fournie :

  • Une seule ligne de texte

Par exemple, pour :

Hello World

Votre sortie devrait être :

Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11

Et pour :

Go interfaces are powerful

Votre sortie devrait être :

Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26

L'idée clé est que votre CountingReader fonctionne parfaitement avec io.ReadAll car il implémente io.Reader. Toute fonction de l'écosystème Go qui accepte un io.Reader fonctionnera avec votre type personnalisé — c'est toute la puissance de la conception basée sur les interfaces.

Aide-mémoire

Les interfaces io.Reader et io.Writer de Go sont fondamentales pour les opérations d'E/S :

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

La méthode Read lit jusqu'à len(p) octets dans la slice et renvoie le nombre d'octets lus. La méthode Write écrit des octets à partir de la slice et renvoie le nombre d'octets écrits.

Tout type implémentant ces méthodes fonctionne avec tout l'écosystème d'E/S de Go. Exemple de lecteur personnalisé :

type RepeatReader struct {
    Char  byte
    Count int
    pos   int
}

func (r *RepeatReader) Read(p []byte) (int, error) {
    if r.pos >= r.Count {
        return 0, io.EOF
    }
    n := 0
    for n < len(p) && r.pos < r.Count {
        p[n] = r.Char
        n++
        r.pos++
    }
    return n, nil
}

Utilisation du lecteur personnalisé avec les fonctions de la bibliothèque standard :

reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data))  // AAAAA

Cette conception permet aux fonctions acceptant io.Reader ou io.Writer de travailler avec des fichiers, des connexions réseau, des tampons ou toute implémentation personnalisée.

Essayez vous-même

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	// Lire l'entrée
	reader := bufio.NewReader(os.Stdin)
	input, _ := reader.ReadString('\n')
	// Supprimer le saut de ligne final s'il est présent
	if len(input) > 0 && input[len(input)-1] == '\n' {
		input = input[:len(input)-1]
	}

	// TODO: Créer un CountingReader avec la chaîne d'entrée
	// countingReader := NewCountingReader(input)

	// TODO: Utiliser io.ReadAll pour lire toutes les données de votre CountingReader
	// data, err := io.ReadAll(countingReader)

	// TODO: Créer un UppercaseWriter
	// upperWriter := NewUppercaseWriter()

	// TODO: Écrire les données dans votre UppercaseWriter
	// upperWriter.Write(data)

	// TODO: Afficher les résultats au format requis :
	// Original: [input]
	// Uppercase: [result from writer]
	// Bytes read: [total bytes]

	// Espace réservé pour éviter l'erreur d'importation non utilisée - à supprimer lors de l'implémentation
	_ = io.EOF
	fmt.Println("Original:", input)
	// fmt.Println("Uppercase:", upperWriter.Result())
	// fmt.Println("Bytes read:", countingReader.Position)
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet