ICS3U

Accueil > Programmer avec Java > Les bases de Java >

Entrées et sorties à la console

Survol et attentes

Pour ajouter de l’interaction à un programme, il faut donner la chance à l’utilisateur de nous fournir une réponse. Dans le contexte d’un programme qui se lance à la console, cela veut dire capter les réponses tapées par l’utilisateur au clavier. Dans cette leçon, nous allons voir comment faire ça en Java.

Définitions
Mot-clé new
un mot-clé qui crée une nouvelle instance d’une classe (qui crée un objet). Ce mot-clé est suivi par une méthode spéciale de la classe appelée le constructeur. Chaque classe définit son propre constructeur, alors il faut connaître les détails de la classe pour créer des objets de cette classe. Votre EDI vous aide avec ces détails. La déclaration complète d’un nouvel objet est Type nom = new Type(arguments, ...);.
Classe Scanner
une classe qui nous permet de lire une source de texte comme la console, un String ou un fichier de texte. Un objet de type Scanner a accès à plusieurs méthodes d’instance pour retourner différents types de données. Un Scanner saisie toujours du texte, donc les méthodes comme nextInt() et nextDouble() convertissent le texte en nombre et peuvent planter si le format des caractères saisis ne correspond pas au type attendu. C’est pourquoi il y a aussi des méthodes comme hasNextInt() et hasNextDouble() pour valider le format avant de tenter une conversion.
Classe Locale
une classe qui permet de spécifier un ensemble de formats régionaux, notamment celui des nombres décimaux. Par défaut, le format dépend du système d’exploitation donc le programme se comportera différemment sur différents ordinateurs. Pour éviter des erreurs de conversion, on peut spécifier un Locale pour chaque Scanner en utilisant la méthode useLocale().
Objet System.in
un objet de type InputStream qui représente l’entrée standard du système d’exploitation, généralement la console. L’avantage est qu’il est toujours défini (ça fonctionne toujours). Le désavantage est que Windows gère les caractères Unicode différemment que le reste du monde, ce qui donne parfois des comportements inattendus et difficiles à résoudre.
Invite de réponse
un message affiché à l’utilisateur pour lui indiquer ce qu’il doit saisir. C’est une bonne pratique de donner des invites de réponse claires et spécifiques pour éviter des erreurs de saisie. Des bonnes invites de réponse rendent toujours l’intéraction plus agréable pour l’utilisateur.

Liste des méthodes de Scanner les plus courantes :

  • next() : saisir le prochain mot (jusqu’à un espace, ignorant les espaces initiaux)
  • nextInt() : saisir le prochain mot et le convertir en int
  • nextDouble() : saisir le prochain mot et le convertir en double
  • hasNextInt() : vérifier si le prochain mot est un int
  • hasNextDouble() : vérifier si le prochain mot est un double
  • nextLine() : saisir la prochaine ligne de texte (jusqu’à un retour à la ligne)… il y a des nuances avec cette méthode que nous explorerons en détail ci-dessous.

Objectifs d’apprentissage

À la fin de cette leçon vous devrez être en mesure de :

Critères de succès

Structure générale d’un programme avec un Scanner

Les Scanner sont utilisés pour lire du texte à partir de différentes sources. Pour commencer, on utilisera un Scanner qui surveille System.in pour complémenter le PrintStream (System.out) qu’on utilise déjà pour afficher des messsages texte à la console. Bref, les Scanner vont nous permettre de lire des réponses de l’utilisateur à la console et d’avoir des programmes interactifs.

Si vous ne l’aviez pas deviné, System.in et System.out sont les entrées et sorties standard de votre système d’exploitation, notamment la console.

La structure globale d’un programme qui utilise un Scanner est la suivante :

1
2
3
4
5
6
7
import java.util.*; // importer la définition de Scanner de java.util

void main() {
    Scanner console = new Scanner(System.in); // déclarer un Scanner pour la console

    // ... le reste du programme
}

Déclarer un Scanner

Le format de la déclaration d’un Scanner est le suivant :

1
Scanner nom = new Scanner(source);

nom est le nom de la variable qui contiendra l’objet Scanner et source est la source de texte que le Scanner lira. Le nom est généralement un reflèt précis de la source.

Pour lire de la console

On utilise System.in, l’entrée standard, comme source.

1
Scanner console = new Scanner(System.in);

Pour lire un String

On utilise un String comme source.

1
2
String text = "Bonjour, le monde!";
Scanner textReader = new Scanner(text);

Fermer un Scanner

On ne ferme jamais le Scanner pour la console parce qu’on s’en sert généralement jusqu’à la fin du programme interactif. Ce Scanner sera fermé automatiquement en arrivant à la fin de la méthode main.

Par contre, c’est important de fermer les Scanner pour toutes les autres sources de texte, comme un fichier ou un String. On ferme un Scanner avec la méthode close() une fois qu’on a finit de le lire. Par exemple :

1
textReader.close();

Introduction aux méthodes de Scanner - extraire les données d’un String

Pour comprendre comment les méthodes de Scanner fonctionnent, on va les tester sur différents String. Les sections plus bas montrent le Scanner dans le contexte d’un programme interactif.

La méthode next() : lire le prochain mot

La méthode next() saisit le prochain mot - plus spécifiquement, le prochain ‘jeton’ - dans le texte. Un jeton est une séquence de caractères qui n’inclut pas d’espaces. Si la prochaine chose dans le texte commence par un ou plusieurs espaces, ces espaces seront ignorés en saisissant le jeton. La méthode next() retourne le jeton saisi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String words = "\n   \tBonjour le monde!";
Scanner wordsReader = new Scanner(words); // Scanner qui utilise le texte 'words' comme source

String word; // variable pour stocker le mot lu

word = wordsReader.next(); // ignore les espaces initiaux puis saisit tout jusqu'au prochain espace, soit "Bonjour"
System.out.println(word);

word = wordsReader.next(); // saisit "le"
System.out.println(word); 

word = wordsReader.next(); // saisit "monde!"
System.out.println(word); 
wordsReader.close();

Les méthodes nextInt() et nextDouble() : convertir le texte en nombre

Peut être que la ligne de texte suivante représente des informations sur une personne, comme son âge et sa moyenne scolaire. Connaissant le format du texte, on peut utiliser les méthodes appropriées pour lire les valeurs.

Les méthodes nextInt() et nextDouble() saisissent le prochain mot et tentent de le convertir en int ou double respectivement. Si le texte ne peut pas être converti en nombre, le programme plantera, alors il faut faire attention de :

  • bien aviser l’utilisateur du format attendu dans un programme interactif
  • bien observer les formats du texte dans des fichiers qu’on veut lire, comme un fichier qui contiendrait plusieurs lignes de données semblables à l’exemple suivant.
1
2
3
4
5
6
7
String info = "Marie 15 85.5";
Scanner infoReader = new Scanner(info);
String nom = infoReader.next(); // saisir le nom
int age = infoReader.nextInt(); // saisir le texte pour le nombre entier
double moyenne = infoReader.nextDouble(); // saisir le nombre décimal
infoReader.close();
System.out.printf("%s a %d ans et sa moyenne est %.1f\n", nom, age, moyenne);

Si le programme fonctionne correctement, il nous donnera cette sortie :

1
Marie a 15 ans et sa moyenne est 85.5

Dans notre contexte spécifique - travailler en français avec des machines configurées en français - il y a quand même un bon risque de plantage parce que le format pour le décimal est différent sur un système anglais (.) que sur un système français (,). À l’interne, Java utilise toujours le . comme décimal, mais il utilisera les formats du système dans ses Scanner pour interpréter les réponses de l’utilisateur à la console.

Donc c’est possible que Java présume que le format 85,5 est le bon et lance une erreur quand il voit plutôt 85.5. Ce n’est pas un bon système, mais il y a une façon de dire dans notre programme quel format on veut utiliser.

Spécifier le format des nombres décimaux

Quand on connaît le format des nombres décimaux dans le texte à lire, ou on dit à l’utilisateur quel format utiliser dans sa réponse interactive, on devrait spécifier ce format explicitement dans le code.

On le fait en utilisant la classe Locale (aussi dans java.util) et les valeurs qu’elle définit par défaut pour différents formats régionaux. Dans notre contexte, on utilise l’une au l’autre des options suivantes :

  • Locale.CANADA : pour utiliser le . comme séparateur décimal
  • Locale.CANADA_FRENCH : pour utiliser la , comme séparateur décimal

Le code précédent devient alors :

1
2
3
4
5
6
7
8
9
10
String info = "Marie 15 85.5";
Scanner infoReader = new Scanner(info);

infoReader.useLocale(Locale.CANADA); // spécifie le format "."

String nom = infoReader.next(); 
int age = infoReader.nextInt(); 
double moyenne = infoReader.nextDouble(); 
infoReader.close();
System.out.printf("%s a %d ans et sa moyenne est %.1f\n", nom, age, moyenne);

Maintenant, le programme fonctionne correctement peu importe l’ordinateur utilisé pour le lancer.

Version interactive : lire la console

En interagissant à la console, c’est important de donner des invites de réponse claires et spécifiques à l’utilisateur afin qu’elle sache quoi taper et avec quel format, si nécessaire.

Voici un exemple en transformant le programme précédent en un programme interactif :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Scanner console = new Scanner(System.in); // source = entrée standard
console.useLocale(Locale.CANADA_FRENCH); // spécifie le format ","

System.out.print("Quel est votre prénom? "); // invite de réponse
String nom = console.next(); 

System.out.print("Quel est votre âge? "); // invite de réponse
int age = console.nextInt(); 

// invite de réponse qui reflète la déclaration du Locale plus haut dans le code
System.out.print("Quelle est votre moyenne? (utilisez le ',' comme décimal) "); 
double moyenne = console.nextDouble(); 

System.out.printf("%s a %d ans et sa moyenne est %.1f\n", nom, age, moyenne);

// notez qu'on n'a pas fermé le Scanner pour la console

Enrichissement : valider la réponse avant de la saisir

Cette section est annotée comme “avancée” parce qu’elle incorpore des éléments de programmation que nous n’avons pas encore vus avec Java, notamment les opérations logiques et les boucles. Si vous ne comprenez pas tout de suite, ne vous inquiétez pas. Vous aurez l’occasion de revoir ces concepts plus tard dans le cours.

Si vous comprenez bien comment formuler une invite de réponse précise et comment assurer la cohérence entre votre invite et ce que vous avez écrit dans votre code, peut-être que vous ne serez pas satisfait de laisser une erreur de frappe par l’utilisateur faire planter votre programme. C’est là qu’on peut commencer à rendre le code plus tolérante aux erreurs en les gérant avant qu’elles ne causent des problèmes.

Pour valider une réponse avant de la saisir, on utilise les méthodes hasNextInt() et hasNextDouble() pour vérifier si le prochain mot est convertible en int ou double respectivement. Si la réponse est valide, on la saisit, la convertit et la conserve. Sinon, on doit vider le Scanner pour préparer la saisie suivante, alors on saisit la réponse comme texte sans la conserver. On peut également afficher un message d’erreur et inviter l’utilisateur à réessayer.

Diagramme de flux pour valider une réponse

1
2
3
4
5
6
7
8
9
10
11
Scanner console = new Scanner(System.in);

System.out.print("Saisissez un nombre entier : ");
while (!console.hasNextInt()) {
    console.next(); // vider le Scanner du jeton incorrect
    System.out.println("Ce n'est pas un nombre entier valide.");
    System.out.print("Saisissez un nombre entier : ");
}

int number = console.nextInt();
System.out.println("Vous avez saisi " + number);

L’opérateur ! est l’inverse logique (“not”). Il transforme true en false et false en true. Dans ce contexte, !console.hasNextInt() signifie “tant que le Scanner n’a pas un nombre entier valide comme prochain jeton”.

Exercices

📚 Tester la compréhension

aucun quiz de vérification des concepts ici encore

🛠️ Pratique

Travaillez dans le répertoire GitHub partagé par votre enseignant pour la pratique et les exercices.