IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Pensez en Java

Comment penser comme un informaticien ?


précédentsommairesuivant

III. Entrées et sorties conversationnelles

Les programmes que nous avons vus jusqu’à maintenant n’affichaient que de simples messages sur la sortie standard (l’écran), ne nécessitant pas beaucoup de vrais calculs. Par contre, dans ce chapitre, nous vous montrerons comment lire des informations saisies depuis l’entrée standard (le clavier) pour calculer un résultat, puis formater ce résultat pour l’affichage sur la sortie standard.

III-A. La classe System

Nous avons déjà utilisé par le passé System.out.println, mais il se peut que vous ne vous soyez pas penché sur la signification de cette instruction. System est une classe fournissant des méthodes relatives au « system » ou en d’autres termes à l’environnement dans lequel le programme s’exécute. Elle fournit également System.out, qui est un objet spécial fournissant une méthode pour l’affichage sur la sortie standard et incluant println.

En fait, nous pouvons utiliser System.out.println pour afficher la valeur de System.out :

 
Sélectionnez
System.out.println( System.out);

Le résultat est :

 
Sélectionnez
java.io.PrintStream@685d72cd

Cette sortie indique que System.out est un PrintStream, qui est défini dans un paquetage appelé java.io. Un paquetage est une collection de classes apparentées ; java.io contient des classes pour les « E/S » qui correspondent aux entrées et sorties standards.

Les nombres et les lettres suivant le symbole @ représentent l’adresse de System.out, représentée comme un nombre en hexadécimal (base 16). L’adresse d’une valeur est sa localisation dans la mémoire de l’ordinateur, qui peut être différente d’un ordinateur à un autre. Dans cet exemple l’adresse est 685d72cd, mais si vous exécutez le même code une autre fois, il se peut que vous ayez un résultat différent.

Comme le montre la Figure 3.1, la classe System est définie dans un fichier s’appelant System.java, et PrintStream est définie dans PrintStream.java. Ces fichiers font partie de la bibliothèque interne de Java, qui représente une immense collection de classes que vous pouvez utiliser dans vos programmes.

Figure 3.1

Figure 3.1 : System.out.println se réfère à la variable out de la classe System, qui est de type PrintStream et qui fournit la méthode println.

III-B. La classe Scanner

La classe System fournit également l’objet spécial System.in, qui est de type InputStream et qui fournit les méthodes de lecture des informations depuis le clavier.

Ces méthodes ne sont pas conviviales à utiliser ; heureusement, Java nous fournit d’autres classes facilitant l’utilisation de la gestion des tâches communes pour les saisies utilisateurs.

Par exemple, Scanner est une classe contenant des méthodes pour traiter en entrée des mots, des nombres ou d’autres types de données. La classe Scanner est présente dans le paquetage java.util, qui est un paquetage contenant des classes très utilisées appelées « classes utilitaires ». Avant de pouvoir utiliser la classe Scanner, vous devez importer le paquetage contenant cette classe comme ceci :

 
Sélectionnez
import java.util.Scanner;

Ce traitement d’import stipule au compilateur que lorsque vous dites Scanner, cela signifie que vous faites référence à la classe définie dans le paquetage java.util.

Cette précision est nécessaire parce qu’il se peut qu’il y ait une autre classe nommée Scanner dans un autre paquetage. Utiliser un traitement d’import vous permet de rendre votre code non ambigu.

Les traitements d’import ne peuvent pas être à l’intérieur de la définition d’une classe.

Par convention, ils se trouvent au tout début du fichier.

Une fois que vous avez créé un Scanner :

 
Sélectionnez
Scanner in = new Scanner(System.in);

Cette ligne déclare une variable de type Scanner nommée in et crée un nouveau Scanner qui prend son entrée de System.in.

La classe Scanner possède une méthode appelée nextLine qui lit une ligne en entrée depuis le clavier et renvoie une String. L’exemple suivant lit deux lignes et les répète en les renvoyant à l’utilisateur.

 
Sélectionnez
import java.util.Scanner; 

public class Echo {
    public static void main(String[] args) {
        String line ;
        Scanner in = new Scanner(System.in) ;
        
        System.out.print("Ecrivez quelque chose : ");
         line = in.nextLine();
         System.out.println("Vous avez écrit : " + line);

        System.out.print("Ecrivez autre chose : ");
        line = in.nextLine();
        System.out.println("Vous avez écrit aussi : " + line);
    }
}

Si vous omettez de déclarer le traitement d’import de la classe Scanner et que vous invoquez cette classe plus bas dans votre code, vous aurez une erreur de compilation comme celle-ci : « cannot find symbol ». Ceci veut dire que le compilateur ne comprend pas ce que signifie le mot Scanner.

Il se peut que vous soyez surpris de pouvoir utiliser la classe System sans effectuer d’import. La classe System appartient au paquetage java.lang « fournissant les classes fondamentales pour la conception et l’élaboration de la programmation en Java ». La classe String fait également partie du paquetage java.lang.

III-C. Structure d’un programme

À ce stade, nous avons parcouru tous les éléments vous permettant de concevoir un programme Java.

La figure 3.2 nous montre l’unité organisationnelle d’un programme Java.

Figure 3.2

Figure 3.2 : les éléments d’un programme Java.

Pour résumer, un paquetage est une collection de classes, définissant des méthodes.

Les méthodes quant à elles contiennent des traitements, certaines d’entre elles contiennent des expressions. Les expressions sont conçues à partir d’éléments basiques d’un programme, éléments basiques incluant des nombres, des noms de variables, des opérateurs, des mots-clefs et des éléments de ponctuation comme des parenthèses, des accolades et des points-virgules.

L’édition standard du Java est fournie avec plusieurs centaines de classes que vous pouvez importer, qui peuvent être à la fois enthousiasmantes ou intimidantes. Vous pouvez parcourir cette bibliothèque à l’adresse http://docs.oracle.com/javase/8/docs/api/.

La plupart des bibliothèques Java sont écrites en Java.

Notez qu’il y a une différence majeure entre le langage Java, définissant la syntaxe et le sens des éléments de la figure 3.2 et la bibliothèque Java qui quant à elle fournit des classes prédéfinies.

III-D. Des pouces aux centimètres

Maintenant, voyons un exemple d’usage courant. Bien que la plupart des humains aient adopté le système métrique pour quantifier les poids et les mesures, certains pays utilisent toujours les unités anglo-saxonnes. Par exemple, lorsque je discute avec des amis résidant en Europe à propos de la météo, les Nord-américains doivent convertir les degrés Celsius en Fahrenheit et vice-versa pour se faire comprendre des Européens. Ils doivent de même convertir en centimètres une taille donnée en pouces.

Nous allons écrire un programme pour permettre cette conversion. Nous utiliserons un objet Scanner pour récupérer une mesure en pouces, la convertir en centimètres et enfin, afficher le résultat. Les lignes suivantes déclarent les variables et créent l’objet Scanner :

 
Sélectionnez
int inch ;
double cm ;
Scanner in = new Scanner(System.in);

L’étape suivante est de demander une saisie clavier à l’utilisateur. Nous utiliserons print au lieu de println pour que l’utilisateur puisse entrer les données sur la même ligne que la demande. Et nous utiliserons la méthode nextInt de la classe Scanner, méthode qui lit la saisie du clavier et la convertit en un entier.

 
Sélectionnez
System.out.print("Combien de pouces: ");
line = in.nextLine();

Ensuite, on multiplie le nombre de pouces par 2.54 – c’est le nombre de centimètres qu’il y a par pouce – et on affiche le résultat :

 
Sélectionnez
cm = inch * 2.54 ;
System.out.print(inch + " pouce =");
System.out.println(inch + " cm");

Ce code fonctionne correctement, mais il a un petit problème. Si un autre développeur lit ce code, il pourrait se demander d’où vient 2,54. Pour faciliter la tâche aux autres (et à vous même dans le futur), il serait mieux d’affecter cette valeur à une variable ayant un nom significatif. Nous démontrerons cela dans la section suivante.

III-E. Littéraux et constantes

Une valeur qui apparaît dans un programme, comme 2,54 (ou " pouce =" ), est appelée un littéral. En général, il n’y a rien de problématique avec les littéraux. Mais quand des nombres comme 2,54 apparaissent dans une expression sans aucune explication, ils rendent le code difficile à maintenir.

Les valeurs comme celles-ci sont parfois appelées nombres magiques (en insinuant qu’être « magique » n’est pas une bonne chose). Une bonne pratique est d’affecter ces nombres magiques à des variables ayant un nom significatif, comme ça :

 
Sélectionnez
double cmPerInch =2.54;
cm = inc * cmPerInch;

Cette version est plus facile à lire et induira moins d’erreurs, mais comporte encore un problème. Les variables peuvent varier, alors que le nombre de centimètres par pouce ne varie pas. Java propose une fonctionnalité qui renforce cette règle, le mot-clef final.

 
Sélectionnez
final double CM_PER_INCH =2.54;

Déclarer qu’une variable est final signifie qu’on ne peut pas changer sa valeur une fois qu’elle a été initialisée. Si vous essayez, le compilateur affichera une erreur. Les variables déclarées final sont appelées des constantes. Par convention, les noms pour les constantes sont entièrement en majuscules avec un underscore (_) entre les mots.

III-F. Sortie formatée

Quand vous affichez un double en utilisant print ou println, cela montre jusqu’à 16 décimales à l’écran :

 
Sélectionnez
System.out.print(4.0 / 3.0);

Le résultat est :

 
Sélectionnez
1.3333333333333333

Vous ne vouliez peut-être pas afficher autant de décimales. System.out fournit une autre méthode, appelée printf, qui permet un plus grand contrôle sur le format. Le « f » dans printf est le début de « formaté ». Voici un exemple :

 
Sélectionnez
System.out.printf("Trois quarts = %.3f ", 4.0 / 3.0);

La première valeur dans les parenthèses est une chaîne formatée qui spécifie comment les données doivent être affichées. Cette chaîne formatée contient du texte ordinaire suivi par une définition de format, qui est une séquence particulière commençant par le signe pourcent. La définition de format %.3f indique que la valeur qui suit devra être affichée comme un nombre décimal, arrondi à 3 chiffres après la virgule.

Le résultat est :

 
Sélectionnez
Trois quarts = 1.333

La chaîne formatée peut contenir autant de définitions de format que l’on souhaite ; voici un exemple avec deux :

 
Sélectionnez
int inch = 100;
double cm = inch * CM_PER_INCH;
System.out.printf("%d pouces = %.3f cm \n", inch, cm);

Le résultat est :

 
Sélectionnez
100 pouces = 254.000000 cm

Comme print, printf ne fait pas de renvoi à la ligne. Donc les chaînes formatées finissent souvent avec le caractère de nouvelle ligne \n.

La définition de format %d affiche des valeurs entières. Les valeurs sont associées avec les définitions de format dans l’ordre, donc inch est affiché en utilisant %d et cm est affiché en utilisant %f.

Apprendre à utiliser les chaînes formatées revient à apprendre un sous-langage dans Java. Il y a beaucoup d’options et les détails peuvent être innombrables. Le tableau 3.1 liste quelques utilisations basiques pour vous donner une idée de comment cela fonctionne. Pour plus de détails, allez voir la documentation de java.util.Formatter. La méthode la plus simple pour trouver de la documentation sur des classes Java et de faire une recherche Web avec « Java » et le nom de la classe.

Tableau 3.1

Tableau 3.1 : exemple de définition de format

III-G. Des centimètres aux pouces

Maintenant, supposons que nous avons une mesure en centimètres et que nous voulons la convertir en pouces de manière précise. Il serait tentant d’écrire :

 
Sélectionnez
Inch = cm / CM_PER_INCH ;     // erreur de syntaxe

Mais cela provoque une erreur, vous aurez quelque chose comme : « Bad types in assignment : from double to int. ». Le problème vient du fait qu’à droite c’est un réel, alors que la variable de gauche est un entier.

La manière la plus simple de convertir un réel en entier est d’utiliser une conversion de type (cast), appelée ainsi en anglais, car cela moule la valeur d’un type dans un autre type. La syntaxe pour la conversion de type est de mettre le nom du type entre parenthèses et de l’utiliser comme opérateur.

 
Sélectionnez
double pi = 3.14159;
int x= (int) pi;

L’opérateur (int) a pour effet de convertir ce qui suit en entier. Dans l’exemple, x prend pour valeur 3. Convertir vers un entier arrondit toujours le nombre d’origine vers le bas. En clair, cela enlève tout simplement la partie décimale.

La conversion de type est prioritaire sur les opérations arithmétiques. Dans l’exemple suivant, la valeur de pi est convertie en un entier avant la multiplication. C’est pourquoi le résultat sera 60.0 et pas 62.0.

 
Sélectionnez
double pi = 3.14159;
int x= (int) pi * 20.0;

En tenant compte de ça, voici comment on peut convertir une mesure en centimètres vers des pouces :

 
Sélectionnez
inch = (int) (cm / CM_PER_INCH);
System.out.printf("%f cm = %d pouces\n", cm, inch);

Les parenthèses après l’opérateur de conversion permettent que la division se fasse avant la conversion de type. Et le résultat est arrondi vers 0 ; nous verrons dans le prochain chapitre comment arrondir les réels vers l’entier le plus proche.

III-H. Opérateur modulo

Allons plus loin avec cet exemple : supposez que vous avez une mesure en pouces et que vous voulez la convertir en pieds et en pouces. Le but est de diviser par 12 (12 étant le nombre de pouces dans un pied) et de garder le reste de la division.

Nous avons déjà vu l’opérateur de division (/), qui calcule le quotient de deux nombres. Si les nombres sont des entiers, cela calcule une division entière. Java fournit aussi l’opérateur modulo (%), qui divise deux nombres et calcule le reste.

En utilisant la division et le modulo, on peut convertir en pieds et pouces comme ça :

 
Sélectionnez
quotient = 76 /12;     // division
reste = 76 % 12;     // modulo

La première ligne donne 6. La seconde ligne, qui se prononce « 76 mod 12 », donne 4. Donc 76 pouces correspondent à 6 pieds et 4 pouces.

L’opérateur modulo ressemble à un caractère pourcent, mais vous pouvez, comme pense-bête, penser que c’est un signe de division (÷) tourné vers la droite.

L’opérateur modulo peut devenir étonnamment très utile. Pa exemple, vous pouvez vérifier qu’un nombre est divisible par un autre : si x % y donne zéro, alors x est divisible par y. Vous pouvez utiliser modulo pour extraire des chiffres d’un nombre : x % 10 extrait le chiffre le plus à droite du nombre x, et x % 100 extrait les deux derniers chiffres de x. Il y a aussi beaucoup d’algorithmes de cryptage qui utilisent l’opérateur modulo.

III-I. Assembler tout cela

À partir de maintenant, vous avez vu assez de Java pour écrire des programmes utiles, résolvant des problèmes de tous les jours. Vous pouvez (1) importer des bibliothèques de classes Java, (2) créer un objet Scanner, (3) récupérer une saisie du clavier, (4) formater un texte de sortie avec printf et (5) diviser et faire un modulo sur des entiers. Maintenant, nous allons rassembler tout ça dans un programme complet :

 
Sélectionnez
import java.util.Scanner; 

/** 
  * Convertit les cm en pieds et pouces
  */ 
public class Convert { 
        public static void main(String[] args) { 
                    double cm; 
                    int feet, inches, remainder; 
                    final double CM_PER_INCH = 2.54; 
                    final int IN_PER_FOOT = 12; 
                    Scanner in = new Scanner(System.in); 
                    
                    // Demande à l’utilisateur et récupère la valeur 
                    System.out.print("Combien de cm exactement? "); 
                    cm = in.nextDouble(); 

                    // convertit et affiche le résultat
                    inches = (int) (cm / CM_PER_INCH); 
                    feet = inches / IN_PER_FOOT; 
                    remainder = inches % IN_PER_FOOT; 
                    System.out.printf("%.2f cm = %d ft, %d in\n", 
                                                               cm, feet, remainder);
        }
 }

Bien que ce ne soit pas obligatoire, toutes les variables et les constantes sont déclarées au début de la procédure main. Cela permet de retrouver plus facilement leur type plus tard et cela aide le lecteur à savoir quelles données sont concernées dans l’algorithme.

Pour aider aussi à la lecture du code, chaque étape majeure est séparée par une ligne blanche et commence par un commentaire. On ajoute aussi un commentaire de documentation (// ou /*...*/), dont nous parlerons plus en détail dans le prochain chapitre.

De nombreux algorithmes, dont Convert, utilisent ensemble la division et le modulo . Dans les deux étapes, on divise par le même nombre (IN_PER_FOOT).

Quand une ligne de code devient trop longue (généralement, plus grande que 80 caractères), une convention de style courante est de couper la ligne en plusieurs lignes. Le lecteur ne doit pas avoir à faire défiler la page horizontalement.

III-J. Les bogues de Scanner

Maintenant que vous avez un peu d’expérience avec Scanner, nous devons vous avertir d’un comportement inattendu. Le code suivant demande aux utilisateurs leur nom et leur âge :

 
Sélectionnez
System.out.print("Quel est votre âge ? "); 
age = in.nextInt(); 
System.out.print("Quel est votre nom ? "); 
name = in.nextLine(); 
System.out.printf("Bonjour %s, %d ans\n", name, age);

Si vous essayez de lancer ce programme exemple, cela ne vous laissera pas saisir votre nom et affichera directement le résultat :

 
Sélectionnez
Quel est votre nom ? Bonjour , 45 ans

Pour comprendre ce qu’il se passe, vous devez comprendre que Scanner ne voit pas la saisie sur plusieurs lignes, comme nous. Au lieu de ça, il récupère un « flux de caractères » comme présenté sur la figure 3.3.

Figure 3.3

La flèche indique le prochain caractère qui va être lu par Scanner. Quand vous appelez nextInt, cela lit les caractères jusqu’à ce que ça arrive à un caractère qui n’est pas un chiffre. La Figure 3.4 montre l’état du flux après avoir appelé nextInt.

Figure 3.4

À ce moment, nextInt renvoie 45. Ensuite, le programme affiche la question « Quel est votre nom ? » et appelle nextLine, qui lit les caractères jusqu’à ce qu’il arrive à une nouvelle ligne. Mais vu que le prochain caractère est déjà une nouvelle ligne (\n), nextLine renvoie une chaîne vide "".

Pour résoudre le problème, vous avez besoin d’un nextLine supplémentaire après le nextInt.

 
Sélectionnez
System.out.print("Quel est votre âge ? "); 
age = in.nextInt(); 
in.nextLine() ;    // lit la nouvelle ligne
System.out.print("Quel est votre nom ? "); 
name = in.nextLine(); 
System.out.printf("Bonjour %s, %d ans\n", name, age);

Cette technique est courante quand on lit un int ou un double qui sont affichés sur leur propre ligne. D’abord vous lisez le nombre et ensuite vous lisez le reste de la ligne, qui est juste un retour à la ligne.

III-K. Vocabulaire

package (paquetage) : un groupe de classes qui sont reliées entre elles.

adress (adresse) : l’emplacement d’une valeur dans la mémoire de l’ordinateur, souvent représenté par un entier hexadécimal.

Library (bibliothèque) : un ensemble de paquetages et de classes disponibles pour être utilisés dans d’autres programmes.

import statement (fonction d’import) : une déclaration permettant aux programmes d’utiliser des classes définies dans d’autres paquetages.

Token : un élément de base d’un programme comme un mot, un espace, un symbole ou un nombre.

literal (littéral) : une valeur qui apparaît dans le code source. Par exemple, "Bonjour" est une chaîne littérale et 74 est un entier littéral.

magic number (nombre magique) : un nombre qui apparaît sans aucune explication dans une expression du code. Il doit généralement être remplacé par une constante.

Constant : une variable, déclarée comme final, dont la valeur ne peut être changée.

format string (chaîne formatée) : une chaîne passée en paramètre à printf pour définir le format de sortie.

format specifier (définition de format) : un code spécial qui commence par le signe pourcent et qui définit le type de données et le format de la valeur correspondante.

type cast (conversion de type) : une opération qui convertit explicitement un type de données vers un autre. En Java, cela se présente sous la forme d’un nom de type entre parenthèses, comme (int).

Modulus (modulo) : un opérateur qui calcule le reste d’une division d’un entier par un autre entier. En Java, on le note avec un signe pourcent ; par exemple, 5 % 2 donne 1.

III-L. Exercices

Le code pour ce chapitre est dans le répertoire ch03 de ThinkJavaCode. Voir la page ??Utilisation des exemples de code pour avoir des instructions (en anglais) pour récupérer le dépôt. Avant de commencer les exercices, nous vous recommandons de compiler et lancer les exemples.

Si vous n’avez pas encore lu l’appendice A.3, c’est le moment. Il décrit l’interface en ligne de commandes, qui est une manière puissante et efficace d’interagir avec votre ordinateur.

Exercice 1 : quand vous utilisez printf, le compilateur Java ne vérifie pas votre chaîne formatée. Regardez ce qu’il se passe si vous essayez d’afficher une valeur de type int en utilisant %f. Et que se passe-t-il si vous affichez un double en utilisant %d ? Et si vous mettez deux définitions de format dans le texte, mais que vous ne mettez qu’une valeur en paramètre ?

Exercice 2 : écrivez un programme qui convertit une température en degrés Celsius en une température en degrés Fahrenheit. Il devra (1) demander à l’utilisateur une valeur, (2) lire un double depuis le clavier, (3) calculer le résultat, (4) formater l’affichage de ce résultat avec un seul chiffre après la virgule. Par exemple, il devrait s’afficher : « 24,0 C = 75,2 F ».

Voici la formule, faites attention à ne pas utiliser la division d’entier!

formule

Exercice 3 :écrivez un programme qui convertit un nombre total de secondes en heures, minutes et secondes. Il devra (1) demander à l’utilisateur une valeur, (2) lire un entier depuis le clavier, (3) calculer le résultat, (4) formater l’affichage de ce résultat avec printf. Par exemple, il devrait s’afficher : « 5000 secondes = 1 heures, 23 minutes et 20 secondes ».

P.-S : - Utilisez l’opérateur modulo.

Exercice 4 : le but de cet exercice est de programmer un jeu « Devine le nombre ». Quand il sera fini, il devra fonctionner comme ça :

 
Sélectionnez
Je pense à un nombre entre 1 et 100 (inclus). Pouvez-vous le deviner ?
Tapez un nombre : 45
Votre réponse est : 45
Le nombre auquel je pensais était : 14
Vous y étiez presque, à 31 près...

Pour choisir un nombre aléatoire, vous pouvez utiliser la classe Random dans java.util. Voici comment elle fonctionne :

 
Sélectionnez
import java.util.Random; 

public class GuessStarter { 
                 public static void main(String[] args) { 
                             // sélectionner un nombre aléatoire 
                            Random random = new Random(); 
                            int number = random.nextInt(100) + 1;
                            System.out.println(number); 
                  }
}

Comme la classe Scanner que nous avons vue dans ce chapitre, Random doit être importée avant son utilisation. Et comme nous l’avons vu avec Scanner, on doit utiliser l’opérateur new pour créer un Random (générateur de nombre).

Ensuite, on peut utiliser la méthode nextInt pour générer un nombre aléatoire. Dans cet exemple, le résulta de nextInt(100) sera un nombre entre 0 et 99, inclus. En ajoutant 1, vous aurez un nombre ente 1 et 100 inclus.

1. La déclaration de GuessStarter est dans un fichier appelé GuessStarter.java, dans le répertoire appelé ch03, dans le dépôt de ce cours.

2. Compilez et lancez le programme.

3.Modifiez le programme pour interroger l’utilisateur, puis utilisez Scanner pour lire la réponse. Compilez et testez le programme.

4. Lisez la réponse de l’utilisateur comme un entier et affichez le résultat. Encore une fois, compilez et testez le programme.

5. Calculez et affichez la différence entre la réponse de l’utilisateur et le nombre qui avait été généré.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Allen B. Downey et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.