Pour rendre des programmes d’exemple plus intéressants, surtout lors de l’enseignement du langage Java au débutants, il est important d’accepter les entrées à partir du clavier et de formater correctement la sortie des programme.
Bien sûr, les programmes modernes utilisent une interface utilisateur graphique pour recueillir les données de l’utilisateur. Cependant, la programmation d’une telle interface nécessite plus d’outils et de techniques qu’on va les présenter dans cet article.
Notre premier objectif est de nous familiariser avec le langage de programmation Java, c’est pourquoi nous utilisons la console pour la lecture des entrées et l’affichage des informations de sortie.
Lecture d’entrées saisies au clavier
Vous avez vu qu’il est facile d’afficher les sorties d’un programme Java vers le “flux de sortie standard” (c’est-à-dire la fenêtre de la console) simplement en appelant System.out.println.
La lecture du “flux d’entrée standard” System.in n’est pas aussi simple. Pour lire l’entrée de la console, vous devez d’abord construire un Scanner qui est attaché à System.in :
Scanner in = new Scanner(System.in);
On a discuté les constructeurs et l’opérateur new en détail dans la série d’articles sur les objets et les classes. Vous pouvez maintenant utiliser les différentes méthodes de la classe Scanner pour lire les entrées. Par exemple, la méthode nextLine lit une ligne d’entrée.
System.out.print("What is your name? "); String name = in.nextLine();
Ici, nous utilisons la méthode nextLine car l’entrée peut contenir des espaces. Pour lire un seul mot (délimité par des espaces), appelez
String firstName = in.next();
Pour lire un entier, utilisez la méthode nextInt.
System.out.print("How old are you? "); int age = in.nextInt();
De même, la méthode nextDouble lit le nombre à virgule flottante suivant.
Le programme dans ci-dessous demande le nom et l’âge de l’utilisateur, puis affiche un message du type
Hello, Cay. Next year, you'll be 57
Enfin, notez la ligne
importer java.util.* ;
au début du programme. La classe Scanner est définie dans le package java.util. Chaque fois que vous utilisez une classe qui n’est pas définie dans le paquet java.lang de base, vous devez utiliser une directive import.
La classe Scanner ne convient pas à la lecture d’un mot de passe à partir de la console car l’entrée est clairement visible par tous. Java 6 introduit une classe Console spécifique à cet effet. Pour lire un mot de passe, utilisez le code suivant :
Console cons = System.console(); String username = cons.readLine("User name: "); char[] passwd = cons.readPassword("Password: ");
Pour des raisons de sécurité, le mot de passe est stocké dans un tableau de caractères plutôt que dans un objet String. Une fois que vous avez fini de traiter le mot de passe, vous devez immédiatement écraser les éléments du tableau avec une valeur quelconque.
Le traitement des entrées avec un objet Console n’est pas aussi pratique qu’avec un objet Scanner, vous devez lire l’entrée ligne par ligne. Il n’existe pas de méthode pour lire les mots ou les chiffres de façon individuelle.
Voici quelque méthodes utiles pour la lecture des entrées au clavier:
java.util.Scanner
Scanner(InputStream in): construit un objet Scanner à partir du flux d’entrée donné.
String nextLine(): lit la ligne suivante de l’entrée.
String next(): lit le mot suivant (délimité par des espaces).
int nextInt()
double nextDouble(): lit et convertit la séquence de caractères suivante qui représente un nombre entier ou un nombre à virgule flottante.
boolean hasNext(): teste s’il y a un autre mot dans l’entrée.
boolean hasNextInt()
boolean hasNextDouble(): teste si la séquence de caractères suivante représente un nombre entier ou un nombre à virgule flottante.
java.lang.System
static Console console(): retourne un objet Console pour interagir avec l’utilisateur via une fenêtre de console si une telle interaction est possible, null sinon. Un objet Console est disponible pour tout programme lancé dans une fenêtre de console. Sinon, la disponibilité dépend du système.
java.io.Console
static char[] readPassword(String prompt, Object… args)
static String readLine(String prompt, Object… args): affiche l’invite de commande et lit l’entrée de l’utilisateur jusqu’à la fin de la ligne de saisie. Les paramètres args peuvent être utilisés pour fournir des arguments de formatage, comme décrit dans la section suivante.
Formatage de la sortie d’un programme en Java
Vous pouvez afficher un nombre x sur la console avec l’instruction System.out.print(x). Cette commande affichera x avec le nombre maximum de chiffres différents de zéro pour ce type. Par exemple,
double x = 10000.0 / 3.0; System.out.print(x);
affiche 3333.3333333333335
C’est un problème si vous voulez afficher, par exemple, les dollars et les cents.
Dans les premières versions de Java, le formatage des nombres était un peu compliqué. Heureusement, Java 5 a ramené la méthode printf de la bibliothèque C. Par exemple, l’appel
System.out.printf("%8.2f", x);
affiche x avec une largeur de la partie entière de 8 caractères et une précision de 2 caractères. C’est-à-dire que l’affichage contient un espace au début et les sept caractères 3333.33.
Vous pouvez fournir plusieurs paramètres à printf. Par exemple :
System.out.printf("Hello, %s. Next year, you'll be %d", name, age);
Chaque spécificateur de format commençant par un caractère % est remplacé par l’argument correspondant. Le caractère de conversion qui termine un spécificateur de format indique le type de la valeur à formater : f indique un nombre à virgule flottante, s indique une chaîne de caractères et d indique un entier décimal. Le tableau A présente tous les caractères de conversion.
Tableau A – Conversions pour printf
Caractère de conversion | Type | Exemple |
d | Entier décimal | 159 |
x | Entier hexadécimal | 9f |
o | Entier octal | 237 |
f | Flottant à virgule fixe | 15.9 |
e | Nombre à virgule flottante au format exponentiel | 1.59e+01 |
g | Flottant général (la plus courte de e et f) | — |
a | Flottant hexadécimal | 0x1.fccdp3 |
s | Chaîne de caractères | Hello |
c | Caractère | H |
b | booléen | true |
h | code de hachage | 42628b2 |
tx or Tx | Date et heure (T force la mise en majuscule) | Annulé, utilisez les classes java.time plutôt. |
% | Le symbole du pourcentage | % |
n | Le séparateur de ligne dépendant de la plate-forme | — |
En outre, vous pouvez spécifier des indicateurs qui contrôlent l’apparence de l’édition formatée. Le tableau B montre tous les indicateurs. Par exemple, l’indicateur virgule ajoute des séparateurs de groupe. C’est à dire,
System.out.printf("%,.2f", 10000.0 / 3.0);
affiche 3,333.33.
Vous pouvez utiliser plusieurs indicateurs, par exemple “%,(.2f” pour utiliser des séparateurs de groupe et inclure les nombres négatifs entre parenthèses.
Tableau B – Flags de printf
Flag | Purpose | Example |
+ | Affiche le signe pour les nombres positifs et négatifs. | +3333.33 |
space | Ajoute un espace avant les nombres positifs. | | 3333.33| |
0 | Ajoute des zéros au début. | 003333.33 |
– | Champ justifié à gauche. | |3333.33 | |
( | Encadre les nombres négatifs entre parenthèses. | (3333.33) |
, | Ajoute des séparateurs de groupes. | 3,333.33 |
# (for fformat) | Inclut toujours un point décimal. | 3,333. |
# (for xor oformat) | Ajoute le préfixe 0x ou 0. | 0xcafe |
$ | Indique l’index de l’argument à formater ; par exemple, %1$d %1$x affiche le premier argument en chiffres décimaux et hexadécimaux. | 159 9F |
< | Formate la même valeur que la spécification précédente ; par exemple, %d %<x affiche le même nombre en décimales et en hexadécimales. | 159 9F |
Vous pouvez utiliser la conversion s pour formater des objets arbitraires. Si un objet arbitraire implémente l’interface Formattable, la méthode formatTo de l’objet est invoquée.
Sinon, la méthode toString est invoquée pour transformer l’objet en chaîne de caractères. Nous discutons de la méthode toStringmethod dans la série d’articles sur l’héritage.
Vous pouvez utiliser la méthode statique String.format pour créer une chaîne formatée sans l’afficher:
String message = String.format("Hello, %s. Next year, you'll be %d", name, age);
Dans un souci d’exhaustivité, nous abordons brièvement les options de formatage de la date et de l’heure de la méthode printf. Pour le nouveau code, vous devez utiliser les méthodes du package java.time.
Mais il se peut que vous rencontriez la classe Date et ses options de formatage associées dans un ancien code. Le format se compose de deux lettres, commençant par t et se terminant par l’une des lettres du tableau C, par exemple,
System.out.printf("%tc", new Date());
affiche la date et l’heure actuelles dans le format
Mon Feb 09 18:05:19 PST 2015
Tableau C – Caractères de conversion de la date et de l’heure
Conversion Character | Type | Example |
c | Date et heure complètes | Mon Feb 09 18:05:19 PST 2015 |
F | Date ISO 8601 | 2015-02-09 |
D | Date formatée États-Unis (mois/jour/année) | 02/09/2015 |
T | Heure format 24 heures | 18:05:19 |
r | Heure format 24 heures | 06:05:19 pm |
R | Heure format 24 heures, pas de secondes | 18:05 |
Y | Année à quatre chiffres (avec zéros au début) | 2015 |
y | Les deux derniers chiffres de l’année (avec les zéros au début) | 15 |
C | Les deux premiers chiffres de l’année (avec les zéros au début) | 20 |
B | Nom complet du mois | February |
b or h | Nom abrégé du mois | Feb |
m | Mois à deux chiffres (avec zéros au début) | 02 |
d | Jour à deux chiffres (avec zéros au début) | 09 |
e | Jour à deux chiffres (sans zéros au début) | 9 |
A | Nom complet du jour de la semaine | Monday |
a | Nom abrégé du jour de la semaine | Mon |
j | Jour de l’année à trois chiffres (avec zéros au début), entre 001 et 366. | 069 |
H | Heure à deux chiffres (avec zéros au début), entre 00 et 23 | 18 |
k | Heure à deux chiffres (sans zéros en tête), entre 0 et 23 | 18 |
I | Heure à deux chiffres (avec zéros en tête), entre 01 et 12 | 06 |
l | Heure à deux chiffres (sans zéros en tête), entre 1 et 12 | 6 |
M | Minutes à deux chiffres (avec zéros en tête) | 05 |
S | Secondes à deux chiffres (avec zéros en tête) | 19 |
L | Millisecondes à trois chiffres (avec zéros en tête) | 047 |
N | Nanosecondes à neuf chiffres (avec zéros en tête) | 047000000 |
p | Marqueur du matin ou de l’après-midi | pm |
z | Décalage numérique RFC 822 par rapport au GMT | -0800 |
Z | Fuseau horaire | PST |
s | Quelques secondes depuis 1970-01-01 00:00:00 GMT | 1078884319 |
Q | Millisecondes secondes depuis 1970-01-01 00:00:00 GMT | 1078884319047 |
Comme vous pouvez le voir dans le tableau C, certains formats ne donnent qu’une partie d’une date donnée – par exemple, seulement le jour ou le mois. Ce serait un peu idiot si vous deviez fournir la date plusieurs fois pour formater chaque pièce.
Pour cette raison, une chaîne de formatage peut indiquer l’indice de l’argument à formater. L’indice doit suivre immédiatement le %, et il doit être terminé par un $. Par exemple,
System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new Date());
affiche: Date d’échéance : Le 9 février 2015
Vous pouvez également utiliser l’indicateur <. Il indique que le même argument que dans la spécification de format précédente doit être utilisé à nouveau. C’est-à-dire, la déclaration
System.out.printf("%s %tB %<te, %<tY", "Due date:", new Date());
donne le même résultat que l’instruction précédente.
Les valeurs du paramètre indice commencent par 1, et non par 0 : %1$… formate le premier argument. Ceci évite la confusion avec l’indicateur 0.
Vous avez maintenant vu toutes les fonctionnalités de la méthode printf. La figure A présente un diagramme syntaxique pour les spécificateurs de format.
Figure A – Syntaxe du spécificateur de format
Le formatage des nombres et des dates est spécifique à chaque région géographique. Par exemple, en Allemagne, le séparateur de groupe est un point, pas une virgule, et Monday est formaté comme Montag.
Entrée et sortie des fichiers
Pour lire à partir d’un fichier, construisez un objet Scanner comme ceci:
Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);
Si le nom de fichier contient des barres obliques inverses, n’oubliez pas de les échapper chacune avec une barre oblique inversée supplémentaire : “c:\\mydirectory\\myfile.txt”.
Ici, nous spécifions l’encodage de caractères UTF-8, qui est commun (mais pas universel) pour les fichiers sur Internet. Vous devez connaître l’encodage des caractères lorsque vous lisez un fichier texte.
Si vous omettez l’encodage des caractères, le “codage par défaut” de l’ordinateur exécutant le programme Java est utilisé. Ce n’est pas une bonne idée – le programme pourrait agir différemment selon l’endroit où il est exécuté.
Vous pouvez maintenant lire à partir du fichier, en utilisant n’importe laquelle des méthodes de la classe Scanner que nous avons déjà décrit. Pour écrire dans un fichier, construisez un objet PrintWriter. Dans le constructeur, fournissez le nom du fichier et l’encodage des caractères:
PrintWriter out = new PrintWriter("myfile.txt", StandardCharsets.UTF_8);
Si le fichier n’existe pas, il est créé. Vous pouvez utiliser les commandes print, println et printf lors de l’affichage sur System.out.
Vous pouvez construire un objet Scanner avec un paramètre chaîne de caractères, mais l’objet Scanner interprète la chaîne comme une donnée, et non comme un nom de fichier. Par exemple, si vous appelez
Scanner in = new Scanner("myfile.txt"); // ERREUR ?
alors le scanner verra dix caractères de données : ‘m’, ‘y’, ‘f’, etc. Ce n’est probablement pas ce qui était prévu dans ce cas-ci.
Lorsque vous spécifiez un nom de fichier relatif, tel que “myfile.txt”, “mydirectory/myfile.txt”, or “../myfile.txt”, le fichier est situé relativement au répertoire dans lequel la machine virtuelle Java a été lancée. Si vous avez lancé votre programme à partir d’un shell de commande, en exécutant
java MyProg
alors le répertoire de départ est le répertoire courant de l’interpréteur de commandes. Cependant, si vous utilisez un environnement de développement intégré, il contrôle le répertoire de départ. Vous pouvez trouver l’emplacement du répertoire avec cet appel:
String dir = System.getProperty("user.dir");
Si vous rencontrez des problèmes lors de la localisation de fichiers, pensez à utiliser des noms de chemins absolus tels que “c:\\mydirectory\\myfile.txt” or “/home/me/mydirectory/myfile.txt”.
Comme vous l’avez vu, vous pouvez accéder aux fichiers aussi facilement que vous pouvez utiliser System.in et System.out. Il n’y a qu’un seul piège : Si vous construisez un objet Scanner avec un fichier qui n’existe pas ou un PrintWriter avec un nom de fichier qui ne peut pas être créé, une exception est générée.
Le compilateur Java considère ces exceptions comme plus graves qu’une exception “divide by zero”.
Pour l’instant, vous devez simplement dire au compilateur que vous êtes conscient de la possibilité d’une exception “input/output”. Pour ce faire, vous marquez la méthode main avec une clause throws, comme ceci :
public static void main(String[] args) throws IOException { Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8); . . . }
Vous avez maintenant vu comment lire et écrire des fichiers contenant des données textuelles. On va publier dans l’avenir des articles sur des sujets plus avancés, tels que le traitement des différents encodages de caractères, le traitement des données binaires, la lecture des répertoires et l’écriture des fichiers zip. So stay tuned 😉
Lorsque vous lancez un programme à partir d’un interpréteur de commandes, vous pouvez utiliser la syntaxe de redirection de votre interpréteur de commandes et joindre tout fichier à System.in et System.out :
java MyProg < myfile.txt > output.txt
Ensuite, vous n’avez pas à vous soucier de la gestion de l’exception IOException.
Voici quelques méthodes utiles:
java.util.Scanner
Scanner(Path p, String encoding): construit un objet Scanner qui lit les données à partir du chemin donné, en utilisant l’encodage de caractères donné.
Scanner(String data): construit un objet Scanner qui lit les données de la chaîne donnée.
java.io.PrintWriter
PrintWriter(String fileName): construit un PrintWriter qui écrit les données dans le fichier avec le nom de fichier donné.
java.nio.file.Path
static Path of(String pathname): construit un chemin à partir du nom du chemin donné.