Menu
Coddy logo textTech

io.Reader et 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 met en valeur une conception basée sur les interfaces grâce à deux interfaces fondamentales : io.Reader et io.Writer. Ces interfaces simples permettent tout, des opérations sur les fichiers aux communications réseau.

L’interface io.Reader ne requiert qu’une seule méthode :

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

Il lit jusqu’à len(p) octets dans la tranche 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 des E/S.

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
}

Maintenant, ce lecteur personnalisé fonctionne avec toute fonction qui attend 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 les rend compatibles 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, en montrant comment ces interfaces fondamentales permettent une composition puissante des opérations d’E/S.

Vous organiserez votre code sur deux fichiers :

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

    Créez une structure CountingReader qui encapsule une chaîne et suit le nombre d’octets lus. Elle doit contenir des champs pour la chaîne source et un indicateur de position. Implémentez la méthode Read(p []byte) (int, error) qui lit les octets de la source dans la tranche 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 reçus en majuscules et les stocke. Ajoutez une méthode Result() string pour récupérer le texte accumulé en majuscules.

    Créez les fonctions constructrices NewCountingReader(source string) *CountingReader et NewUppercaseWriter() *UppercaseWriter afin d’initialiser correctement vos types.

  • main.go : montrez vos types d’E/S personnalisés en fonctionnement avec la bibliothèque standard.

    Lisez une chaîne en entrée, puis utilisez votre CountingReader avec io.ReadAll pour lire toutes les données. Faites passer ces données dans votre UppercaseWriter à l’aide de 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, avec :

Hello World

Votre sortie doit être :

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

Et avec :

Go interfaces are powerful

Votre sortie doit être :

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

L’idée essentielle est que votre CountingReader fonctionne parfaitement avec io.ReadAll parce qu’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 d’une conception fondée sur les interfaces.

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 dans le format requis :
	// Original: [input]
	// Uppercase : [résultat du writer]
	// Bytes read : [total d'octets]

	// Placeholder pour éviter l'erreur d'import non utilisé - à 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

Entraînez-vous par vous-même : Compilateur Go en ligne