Le package standard fmt de Go fournit des fonctions d'entrée/sortie formatées, s'inspirant des fonctionnalités de printf et scanf du langage C. Il est essentiel pour interagir avec l'utilisateur ou d'autres flux de données. Ce package se divise principalement en deux catégories : l'émission de données (sortie) et la lecture de données (entrée).
Fonctions de Sortie du Package fmt
La bibliothèque fmt offre plusieurs fonctions pour afficher des informations, dont les plus courantes sont Print, Fprint, Sprint et Errorf.
Les fonctions Print, Printf et Println
Ces fonctions dirigent leur sortie vers le flux de sortie standard du système. Elles se ditsinguent par leur capacité de formatage et l'ajout de retours à la ligne :
Printaffiche des valeurs sans formatage spécifique.Printfpermet un formatage précis des chaînes de caractères via des spécificateurs.Printlnajoute automatiquement un saut de ligne après les valeurs affichées.
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
La puissance de Printf réside dans ses spécificateurs de formatage.
Spécificateurs de Formatage
Les fonctions de la série *printf utilisent un argument format pour structurer la sortie. Voici une ventilation des spécificateurs par type de variable :
Spécificateurs Génériques
%v: Représentation par défaut de la valeur.%+v: Similaire à%v, mais ajoute les noms de champs pour les structures.%#v: Représentation de la valeur en syntaxe Go.%T: Affiche le type de la valeur.%%: Imprime un signe de pourcentage littéral.
package main
import "fmt"
func main() {
nombre := 123
estVrai := true
produit := struct { nom string }{nom: "Ordinateur Portable"}
fmt.Printf("Valeur par défaut : %v\n", nombre) // 123
fmt.Printf("Valeur par défaut : %v\n", estVrai) // true
fmt.Printf("Structure avec noms de champs : %+v\n", produit) // {nom:Ordinateur Portable}
fmt.Printf("Syntaxe Go de la structure : %#v\n", produit) // struct { nom string }{nom:"Ordinateur Portable"}
fmt.Printf("Type de la valeur : %T\n", produit) // struct { nom string }
fmt.Printf("Pourcentage littéral : 50%%\n") // 50%
}
Spécificateurs pour Booléens
%t: Affichetrueoufalse.
package main
import "fmt"
func main() {
estActif := true
fmt.Printf("État : %t\n", estActif) // true
}
Spécificateurs pour Entiers
%b: Représentation binaire.%c: Caractère Unicode correspondant à la valeur.%d: Représentation décimale.%o: Représentation octale.%x: Représentation hexadécimale (minuscules).%X: Représentation hexadécimale (majuscules).%U: Format Unicode :U+XXXX.%q: Littéral de caractère Go entre guillemets simples (échappement si nécessaire).
package main
import "fmt"
func main() {
valeurASCII := 70 // 'F'
fmt.Printf("Binaire : %b\n", valeurASCII) // 1000110
fmt.Printf("Caractère : %c\n", valeurASCII) // F
fmt.Printf("Décimal : %d\n", valeurASCII) // 70
fmt.Printf("Octal : %o\n", valeurASCII) // 106
fmt.Printf("Hex (minuscules) : %x\n", valeurASCII) // 46
fmt.Printf("Hex (majuscules) : %X\n", valeurASCII) // 46
fmt.Printf("Unicode : %U\n", valeurASCII) // U+0046
fmt.Printf("Littéral de caractère : %q\n", '€') // '€'
}
Spécificateurs pour Flottants et Nombres Complexes
%b: Notation scientifique binaire sans décimales, ex:-123456p-78.%e: Notation scientifique (minuscules), ex:-1.234e+78.%E: Notation scientifique (majuscules), ex:-1.234E+78.%f: Point fixe décimal sans exposant, ex:123.456.%F: Équivalent à%f.%g: Utilise%eou%fselon ce qui est le plus concis.%G: Utilise%Eou%Fselon ce qui est le plus concis.
package main
import "fmt"
func main() {
temperature := 28.5678
fmt.Printf("Flottant binaire : %b\n", temperature) // 6432326759160576p-48
fmt.Printf("Scientifique (e) : %e\n", temperature) // 2.856780e+01
fmt.Printf("Scientifique (E) : %E\n", temperature) // 2.856780E+01
fmt.Printf("Fixe : %f\n", temperature) // 28.567800
fmt.Printf("Générique (g) : %g\n", temperature) // 28.5678
fmt.Printf("Générique (G) : %G\n", temperature) // 28.5678
}
Spécificateurs pour Chaînes de Caractères et Slices de Bytes
%s: Affiche directement la chaîne ou le slice de bytes.%q: Littéral de chaîne Go entre guillemets doubles (échappement si nécessaire).%x: Chaque octet en hexadécimal (minuscules).%X: Chaque octet en hexadécimal (majuscules).
package main
import "fmt"
func main() {
phrase := "Salut Go"
fmt.Printf("Chaîne simple : %s\n", phrase) // Salut Go
fmt.Printf("Littéral de chaîne : %q\n", phrase) // "Salut Go"
fmt.Printf("Hexadécimal (octets) : %x\n", phrase) // 53616c757420476f
fmt.Printf("Hexadécimal (majuscules) : %X\n", phrase) // 53616C757420476F
}
Spécificateurs pour Pointeur
%p: Représentation hexadécimale avec le préfixe0x.%#p: Représentation hexadécimale sans le préfixe0x.
package main
import "fmt"
func main() {
compteur := 50
fmt.Printf("Adresse avec 0x : %p\n", &compteur) // 0xc000...
fmt.Printf("Adresse sans 0x : %#p\n", &compteur) // c000...
}
Contrôle de la Largeur et de la Précision
La largeur est définie par un nombre décimal suivant le signe pourcent. Si elle est omise, aucune information de remplissage n'est ajoutée au-delà du nécessaire. La précision est spécifiée par un point suivi d'un nombre décimal après la largeur (facultatif). Si la précision est omise, une précision par défaut est utilisée ; un point sans nombre indique une précision de zéro.
| Spécificateur | Description |
|---|---|
%f |
Largeur et précision par défaut. |
%10f |
Largeur de 10 caractères, précision par défaut. |
%.3f |
Largeur par défaut, 3 chiffres après la virgule. |
%10.3f |
Largeur de 10 caractères, 3 chiffres après la virgule. |
%10.f |
Largeur de 10 caractères, précision de 0 (nombre entier). |
package main
import "fmt"
func main() {
valeurDecimale := 3.14159265
fmt.Printf("Défaut : %f\n", valeurDecimale) // 3.141593
fmt.Printf("Largeur 10 : %10f\n", valeurDecimale) // 3.141593
fmt.Printf("Précision 2 : %.2f\n", valeurDecimale) // 3.14
fmt.Printf("Largeur 10, Précision 2 : %10.2f\n", valeurDecimale) // 3.14
fmt.Printf("Largeur 10, Précision 0 : %10.f\n", valeurDecimale) // 3
}
Flags Additionnels
'+': Affiche toujours le signe numérique ; pour%q(%+q), produit une sortie entièrement ASCII (avec échappement).' '(espace) : Pour les nombres positifs, ajoute un espace devant (au lieu de rien) ; pour les chaînes avec%xou%X(% xou% X), ajoute des espaces entre les octets imprimés.'-': Remplit les espaces sur la droite de la sortie au lieu de la gauche (alignement à gauche par défaut).'#': Ajoute0pour les octaux (%#o),0xpour les hexadécimaux (%#x) ou0X(%#X) ; retire le0xpour les pointeurs (%#p) ; pour%q(%#q), imprime le littéral Go entre guillemets simples ; pour%U(%#U), affiche le littéral Go entre espaces et guillemets simples.'0': Utilise des zéros pour le remplissage au lieu d'espaces ; pour les types numériques, les zéros de remplissage sont placés après le signe.
Les fonctions Fprint, Fprintf et Fprintln
Ces fonctions dirigent leur sortie vers une varialbe implémentant l'interface io.Writer. Elles sont couramment utilisées pour écrire dans des fichiers ou d'autres flux personnalisés.
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
Un exemple typique est l'écriture dans un fichier :
package main
import (
"fmt"
"io"
"os"
)
func main() {
// Écrire dans la sortie standard (qui est un io.Writer)
fmt.Fprintln(os.Stdout, "Ceci est envoyé à la console.")
fichier, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("Erreur lors de l'ouverture du fichier :", err)
return
}
defer func(closer io.Closer) {
err := closer.Close()
if err != nil {
fmt.Println("Erreur lors de la fermeture du fichier :", err)
}
}(fichier)
utilisateur := "Alice"
// Écrire des données formatées dans le fichier
_, err = fmt.Fprintf(fichier, "L'utilisateur %s s'est connecté à %s.\n", utilisateur, "192.168.1.1")
if err != nil {
fmt.Println("Erreur lors de l'écriture dans le fichier :", err)
}
}
Les fonctions Sprint, Sprintf et Sprintln
Contrairement aux fonctions Print et Fprint, ces fonctions ne produisent pas de sortie directe. Elles formatent les données et renvoient le résultat sous forme de chaîne de caractères.
func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string
package main
import "fmt"
func main() {
messageInitial := fmt.Sprint("Initialisation du système.")
nom := "Bob"
age := 30
infoUtilisateur := fmt.Sprintf("Utilisateur: %s, Âge: %d ans.", nom, age)
logEvenement := fmt.Sprintln("Événement enregistré.")
fmt.Println(messageInitial) // Initialisation du système.
fmt.Println(infoUtilisateur) // Utilisateur: Bob, Âge: 30 ans.
fmt.Println(logEvenement) // Événement enregistré.
}
La fonction Errorf
La fonction Errorf génère une chaîne formatée et la renvoie en tant qu'objet error. Elle est couramment utilisée pour créer des messages d'erreur personnalisés.
func Errorf(format string, a ...interface{}) error
Un cas d'utilisation moderne est l'encapsulation d'erreurs (Error Wrapping) grâce au spécificateur %w, introduit dans Go 1.13. Cela permet de chaîner des erreurs, facilitnat leur inspection ultérieure.
package main
import (
"errors"
"fmt"
)
func main() {
errInterne := errors.New("échec de la connexion à la base de données")
errExterne := fmt.Errorf("impossible de traiter la requête: %w", errInterne)
fmt.Println(errExterne) // impossible de traiter la requête: échec de la connexion à la base de données
if errors.Is(errExterne, errInterne) {
fmt.Println("L'erreur externe enveloppe l'erreur interne.")
}
}
Fonctions d'Entrée du Package fmt
Le package fmt propose également Scan, Scanf et Scanln pour lire les entrées de l'utilisateur depuis le flux standard.
Les fonctions Fscan, Fscanf et Fscanln
Ces fonctions sont l'équivalent des fonctions Scan mais lisent les données depuis un io.Reader spécifié, plutôt que l'entrée standard.
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
Les fonctions Sscan, Sscanf et Sscanln
Ces fonctions sont l'équivalent des fonctions Scan mais lisent les données depuis une chaîne de caractères fournie, au lieu de l'entrée standard.
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
La fonction Scan
fmt.Scan analyse les données lues depuis l'entrée standard. Elle stocke les données séparées par des espaces blancs dans les arguments fournis. Les retours à la ligne sont traités comme des espaces blancs.
- La fonction renvoie le nombre d'éléments scannés avec succès et toute erreur rencontrée.
- Une erreur est retournée si le nombre d'éléments lus est inférieur au nombre d'arguments fournis.
func Scan(a ...interface{}) (n int, err error)
package main
import "fmt"
func main() {
var (
prenom string
age int
ville string
)
fmt.Print("Entrez votre prénom, âge et ville (séparés par des espaces) : ")
_, err := fmt.Scan(&prenom, &age, &ville)
if err != nil {
fmt.Println("Erreur de lecture :", err)
return
}
fmt.Printf("Données lues : Prénom: %s, Âge: %d, Ville: %s\n", prenom, age, ville)
}
Exemple d'interaction :
Entrez votre prénom, âge et ville (séparés par des espaces) : Paul 35 Paris
Données lues : Prénom: Paul, Âge: 35, Ville: Paris
La fonction Scanf
fmt.Scanf lit le texte de l'entrée standard en se basant sur un format spécifique fourni via l'argument format. Elle stocke les valeurs lues dans les arguments passés.
func Scanf(format string, a ...interface{}) (n int, err error)
Scanf exige que l'entrée corresponde précisément au format spécifié, y compris les caractères littéraux.
package main
import "fmt"
func main() {
var (
nomArticle string
quantite int
prixUnite float32
)
fmt.Print("Entrez l'article au format 'Article: [nom] Quantité: [qty] Prix: [prix]' : ")
_, err := fmt.Scanf("Article: %s Quantité: %d Prix: %f", &nomArticle, &quantite, &prixUnite)
if err != nil {
fmt.Println("Erreur de lecture formatée :", err)
return
}
fmt.Printf("Détails de l'article : %s, Quantité: %d, Prix unitaire: %.2f\n", nomArticle, quantite, prixUnite)
}
Exemple d'interaction :
Entrez l'article au format 'Article: [nom] Quantité: [qty] Prix: [prix]' : Article: Clavier Quantité: 2 Prix: 75.50
Détails de l'article : Clavier, Quantité: 2, Prix unitaire: 75.50
Si l'entrée ne respecte pas le format :
Entrez l'article au format 'Article: [nom] Quantité: [qty] Prix: [prix]' : Clavier 2 75.50
Erreur de lecture formatée : expected 'Article:', got 'Clavier'
La fonction Scanln
fmt.Scanln est similaire à Scan, mais elle arrête de scanner dès qu'un caractère de nouvelle ligne est rencontré. La dernière donnée lue doit être suivie d'un retour à la ligne ou de la fin du flux.
func Scanln(a ...interface{}) (n int, err error)
Cette fonction est souvent préférée pour lire des lignes complètes de données.
Utilisation de bufio.NewReader pour des lignes avec des espaces
Pour lire une ligne entière d'entrée qui peut contenir des espaces (comme une phrase), les fonctions Scan ne sont pas toujours idéales car elles séparent les entrées par des espaces. Le package bufio offre une solution robuste.
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin) // Crée un lecteur depuis l'entrée standard
fmt.Print("Veuillez saisir une phrase : ")
saisie, err := reader.ReadString('\n') // Lit jusqu'au caractère de nouvelle ligne
if err != nil {
fmt.Println("Erreur de lecture :", err)
return
}
saisie = strings.TrimSpace(saisie) // Supprime les espaces blancs et le retour à la ligne
fmt.Printf("Vous avez saisi : %#v\n", saisie)
}