Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 7

Lecture d'entrée utilisateur avec la classe Scanner

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons appris à piloter l'exécution de nos programmes avec les structures de contrôle comme if, switch et les différentes boucles (for, while). Ces outils sont la base de toute logique algorithmique.

Aujourd'hui, nous allons franchir une étape clé pour rendre nos applications plus dynamiques et interactives. L'objectif est d'apprendre à lire une entrée utilisateur depuis la console avec la classe Scanner. Cela nous permettra de créer des programmes qui réagissent aux données fournies par l'utilisateur en temps réel, une compétence indispensable pour tout développeur. Nous verrons comment l'utiliser, comment lire différents types de données, et surtout, comment éviter les pièges courants pour écrire du code robuste.

1. Introduction à la classe Scanner

Pour lire des données depuis une source d'entrée (comme le clavier, un fichier, etc.), Java nous fournit la classe java.util.Scanner. C'est l'outil le plus simple et le plus courant pour débuter avec l'interaction utilisateur en console.

Pour l'utiliser, il faut d'abord créer un objet Scanner et le "brancher" sur le flux d'entrée standard, System.in, qui représente le clavier.

La vidéo suivante introduit la classe, son package et la manière de l'importer.

TUTO Java : Utilisation de la classe java.util.Scanner

Regardez ce court extrait de la vidéo "TUTO Java : Utilisation de la classe java.util.Scanner" de Dominique Liard.

Concentrez-vous sur la présentation de la classe, notamment le package java.util et l'importation automatique dans l'IDE.

Voici le code de base pour initialiser un Scanner :

import java.util.Scanner; // Ne pas oublier l'import !

public class MonProgrammeInteractif {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Veuillez entrer du texte :");
        // ... ici, on utilisera le scanner ...
        
        scanner.close(); // Important : toujours fermer le scanner !
    }
}

Bonne pratique : try-with-resources

Fermer manuellement les ressources comme Scanner avec .close() est une source fréquente d'erreurs (oubli de l'appel, exception qui empêche l'appel...). Depuis Java 7, il existe une syntaxe bien plus sûre et professionnelle : le bloc try-with-resources. Il garantit que la ressource sera automatiquement fermée à la fin du bloc, quoi qu'il arrive.

C'est une pratique que vous devez adopter dès maintenant.

TUTO Java : Utilisation de la classe java.util.Scanner

Retournez sur la vidéo précédente pour découvrir cette pratique essentielle.

Regardez l'explication du try-with-resources. Notez comment le Scanner est déclaré entre parenthèses après le try.

Le code précédent devient donc :

import java.util.Scanner;

public class MonProgrammeInteractif {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            System.out.println("Veuillez entrer du texte :");
            // ... tout notre code utilisant le scanner vient ici ...
        }
        // Pas besoin de scanner.close(), Java s'en charge pour nous !
    }
}

2. Lire différents types de données

Le Scanner offre des méthodes dédiées pour lire facilement les types de données les plus courants.

Lire une ligne de texte : nextLine()

Pour lire une chaîne de caractères jusqu'au prochain retour à la ligne (quand l'utilisateur appuie sur "Entrée"), on utilise nextLine().

Utilisation de la classe java.util.Scanner

Cet article de Koor.fr montre un exemple concret d'utilisation de nextLine() pour un système de login simple.

Lisez la section "La méthode nextLine" et analysez l'exemple de code fourni.

Exemple simple :

try (Scanner scanner = new Scanner(System.in)) {
    System.out.print("Quel est votre nom ? ");
    String nom = scanner.nextLine();
    System.out.println("Bonjour, " + nom + " !");
}

Lire des types primitifs : nextInt(), nextDouble(), etc.

Scanner simplifie grandement la lecture de nombres ou d'autres types. Il se charge de la conversion (le "parsing") pour nous.

  • nextInt() pour lire un int
  • nextDouble() pour lire un double
  • nextBoolean() pour lire un boolean
  • et bien d'autres (nextFloat(), nextByte(), etc.).

TUTO Java : Utilisation de la classe java.util.Scanner

Cet extrait de la vidéo de D. Liard explique comment lire des données typées.

Regardez la partie sur la lecture de types différents des chaînes de caractères.

Exemple :

try (Scanner scanner = new Scanner(System.in)) {
    System.out.print("Entrez votre âge : ");
    int age = scanner.nextInt();
    System.out.println("Vous aurez " + (age + 10) + " ans dans 10 ans.");
}

3. Gérer les pièges courants

L'utilisation de Scanner cache un piège très fréquent dans lequel presque tous les débutants tombent. Le comprendre maintenant vous évitera bien des maux de tête.

Le conflit entre nextInt() et nextLine()

Regardez attentivement l'image suivante. Le programme demande l'âge, puis le nom. Pourtant, après avoir entré l'âge, le programme se termine sans attendre la saisie du nom. Pourquoi ?

Ce code illustre le problème : `nextInt()` lit le nombre '35', mais laisse le caractère de nouvelle ligne (créé par la touche "Entrée") dans le flux d'entrée. Le `nextLine()` suivant lit immédiatement cette ligne vide et poursuit l'exécution sans attendre une nouvelle saisie.

L'explication technique :

  • Quand vous tapez 35 puis Entrée, le flux d'entrée contient "35\n".
  • nextInt() est "intelligent" : il lit et consomme uniquement les caractères numériques ("35") et s'arrête là.
  • Le caractère \n reste dans le "tampon" (buffer) d'entrée.
  • L'appel suivant à nextLine() trouve ce \n, le consomme et considère que l'utilisateur a entré une ligne vide.

La solution :
Après un appel à nextInt() (ou nextDouble(), etc.), si vous prévoyez de lire une ligne entière avec nextLine(), vous devez "vider" le tampon en ajoutant un appel à scanner.nextLine() juste après la lecture du nombre.

Comment analyser l'entrée utilisateur en Java

Ce tutoriel de LabEx.io explique très clairement ce problème et sa solution.

Concentrez-vous sur la section "Analyse avec Scanner". Repérez la ligne de code commentée // Consomme le retour à la ligne. C'est la clé pour résoudre le problème.

Voici le code corrigé :

try (Scanner scanner = new Scanner(System.in)) {
    System.out.print("Entrez votre âge : ");
    int age = scanner.nextInt();

    scanner.nextLine(); // <-- LA SOLUTION ! On consomme le \n restant.

    System.out.print("Entrez votre nom : ");
    String name = scanner.nextLine();
    
    System.out.println("Bonjour, " + name + ". Vous avez " + age + " ans.");
}

4. Écrire du code robuste : Gérer les erreurs de saisie

Que se passe-t-il si un utilisateur tape "vingt" au lieu de "20" quand votre programme attend un entier avec nextInt() ? Votre programme va planter en levant une exception InputMismatchException. Un programme professionnel ne doit pas planter à la moindre erreur de l'utilisateur.

Il y a deux approches pour gérer cela.

Approche 1 : Vérifier avant de lire (hasNext...())

La manière la plus "propre" est de vérifier si la prochaine donnée correspond au type attendu avant même d'essayer de la lire. Pour cela, Scanner propose les méthodes hasNextInt(), hasNextDouble(), etc., qui retournent un booléen.

try (Scanner scanner = new Scanner(System.in)) {
    System.out.print("Veuillez saisir un nombre entier : ");
    
    // On boucle tant que l'entrée n'est pas un entier
    while (!scanner.hasNextInt()) {
        System.out.println("Ce n'est pas un entier. Veuillez réessayer :");
        scanner.next(); // Important : on consomme l'entrée invalide pour ne pas boucler à l'infini
    }
    
    int nombre = scanner.nextInt();
    System.out.println("Vous avez saisi le nombre : " + nombre);
}

Approche 2 : Gérer l'erreur après coup (try-catch)

Une autre approche, plus générale en Java, est de "tenter" l'opération (try) et de "rattraper" l'erreur (catch) si elle se produit. Nous verrons la gestion des exceptions en détail dans le module 5, mais voici un aperçu de son application ici, car c'est une technique très courante.

Comment analyser l'entrée utilisateur en Java

La section "Gestion des erreurs" du tutoriel de LabEx.io montre un excellent exemple de gestion des erreurs avec try-catch dans une boucle.

Analysez l'exemple de code InputErrorHandlingDemo. Observez comment le try-catch est placé à l'intérieur d'une boucle while pour redemander la saisie en cas d'erreur.

Voici un exemple combinant cette approche avec notre problème :

try (Scanner scanner = new Scanner(System.in)) {
    int age = 0;
    while (true) { // Boucle infinie qui sera cassée par 'break'
        try {
            System.out.print("Entrez votre âge : ");
            age = scanner.nextInt();
            if (age > 0) { // On ajoute une validation métier
                break; // Sort de la boucle si la saisie est réussie et valide
            } else {
                System.out.println("L'âge doit être positif.");
            }
        } catch (java.util.InputMismatchException e) {
            System.out.println("Entrée invalide. Veuillez entrer un nombre entier.");
            scanner.nextLine(); // On vide le buffer de l'entrée invalide
        }
    }
    System.out.println("Âge valide saisi : " + age);
}

Cette seconde approche est plus puissante car elle permet de gérer différents types d'erreurs au même endroit.


5. Exercice pratique

Il est temps de mettre tout cela en pratique ! Créez une nouvelle classe ProfilUtilisateur.

Objectif : Écrire un programme qui demande à l'utilisateur :

  1. Son prénom (ex: "Jean").
  2. Son nom de famille (ex: "Dupont").
  3. Son année de naissance (ex: 1990).
  4. S'il est un développeur (une réponse true ou false).

Contraintes :

  1. Utilisez un Scanner à l'intérieur d'un bloc try-with-resources.
  2. Assurez-vous que l'année de naissance est bien un nombre entier. En cas d'erreur, le programme doit redemander la saisie jusqu'à ce qu'elle soit correcte. Utilisez l'approche de votre choix (hasNextInt() ou try-catch).
  3. Gérez correctement la lecture du nom de famille après celle de l'année de naissance pour ne pas tomber dans le piège nextInt()/nextLine().
  4. À la fin, le programme doit afficher un résumé, par exemple :
    "Profil créé : Jean DUPONT, né en 1990. Statut développeur : true." (Notez le nom de famille en majuscules).

Astuce : Pour lire la valeur booléenne, vous pouvez utiliser la méthode scanner.nextBoolean().

Cliquez ici pour voir une proposition de solution
import java.util.InputMismatchException;
import java.util.Scanner;

public class ProfilUtilisateur {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            System.out.print("Entrez votre prénom : ");
            String prenom = scanner.nextLine();

            int anneeDeNaissance = 0;
            while (true) {
                System.out.print("Entrez votre année de naissance : ");
                if (scanner.hasNextInt()) {
                    anneeDeNaissance = scanner.nextInt();
                    if (anneeDeNaissance > 1900 && anneeDeNaissance < 2024) {
                        break; // Entrée valide, on sort de la boucle
                    } else {
                        System.out.println("Année invalide. Veuillez entrer une année réaliste.");
                    }
                } else {
                    System.out.println("Ce n'est pas un nombre. Veuillez réessayer.");
                    scanner.next(); // Consomme l'entrée invalide
                }
            }

            scanner.nextLine(); // INDISPENSABLE : consomme le \n après nextInt()

            System.out.print("Entrez votre nom de famille : ");
            String nom = scanner.nextLine();
            
            boolean estDeveloppeur = false;
            while(true) {
                try {
                    System.out.print("Êtes-vous un développeur (true/false) ? ");
                    estDeveloppeur = scanner.nextBoolean();
                    break;
                } catch (InputMismatchException e) {
                    System.out.println("Veuillez répondre par 'true' ou 'false'.");
                    scanner.next(); // Consomme l'entrée invalide
                }
            }

            System.out.println("\n--- Profil créé ---");
            System.out.printf("Nom complet : %s %s%n", prenom, nom.toUpperCase());
            System.out.printf("Année de naissance : %d%n", anneeDeNaissance);
            System.out.printf("Statut développeur : %b%n", estDeveloppeur);
        }
    }
}

Conclusion

Félicitations ! Vous savez maintenant comment faire communiquer vos programmes avec l'utilisateur. C'est une compétence fondamentale qui vous servira dans de nombreux contextes, notamment pour créer des outils en ligne de commande ou pour tester rapidement des fonctionnalités.

Points clés à retenir :

  • La classe java.util.Scanner est l'outil de base pour lire l'entrée console.
  • Utilisez systématiquement le bloc try-with-resources pour une gestion sûre des ressources.
  • nextLine() lit une ligne entière, tandis que nextInt(), nextDouble(), etc., lisent des types spécifiques.
  • Méfiez-vous du piège nextInt() suivi de nextLine() et n'oubliez pas d'ajouter un scanner.nextLine() pour vider le tampon.
  • Pour un code robuste, validez les entrées avec hasNext...() ou gérez les erreurs avec try-catch.

Jusqu'à présent, nous avons manipulé des données uniques (un nom, un âge...). Mais comment faire pour stocker une liste de courses, des scores de joueurs, ou les noms de tous les utilisateurs d'un système ? Dans la prochaine leçon, nous aborderons la gestion de collections de données en commençant par la structure la plus fondamentale en Java : les tableaux.