Lecture d’entrées et formatage de sorties

Lecture d'entrées et formatage de sorties

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 conversionTypeExemple
dEntier décimal159
xEntier hexadécimal9f
oEntier octal237
fFlottant à virgule fixe15.9
eNombre à virgule flottante au format exponentiel1.59e+01
gFlottant général (la plus courte de e et f)
aFlottant hexadécimal0x1.fccdp3
sChaîne de caractèresHello
cCaractèreH
bbooléentrue
hcode de hachage42628b2
tx or TxDate et heure (T force la mise en majuscule)Annulé, utilisez les classes java.time plutôt.
%Le symbole du pourcentage%
nLe 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

FlagPurposeExample
+Affiche le signe pour les nombres positifs et négatifs.+3333.33
spaceAjoute un espace avant les nombres positifs.| 3333.33|
0Ajoute 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 CharacterTypeExample
cDate et heure complètesMon Feb 09 18:05:19 PST 2015
FDate ISO 86012015-02-09
DDate formatée États-Unis (mois/jour/année)02/09/2015
THeure format 24 heures18:05:19
rHeure format 24 heures06:05:19 pm
RHeure format 24 heures, pas de secondes18:05
YAnnée à quatre chiffres (avec zéros au début)2015
yLes deux derniers chiffres de l’année (avec les zéros au début)15
CLes deux premiers chiffres de l’année (avec les zéros au début)20
BNom complet du moisFebruary
b or hNom abrégé du moisFeb
mMois à deux chiffres (avec zéros au début)02
dJour à deux chiffres (avec zéros au début)09
eJour à deux chiffres (sans zéros au début)9
ANom complet du jour de la semaineMonday
aNom abrégé du jour de la semaineMon
jJour de l’année à trois chiffres (avec zéros au début), entre 001 et 366.069
HHeure à deux chiffres (avec zéros au début), entre 00 et 2318
kHeure à deux chiffres (sans zéros en tête), entre 0 et 2318
IHeure à deux chiffres (avec zéros en tête), entre 01 et 1206
lHeure à deux chiffres (sans zéros en tête), entre 1 et 126
MMinutes à deux chiffres (avec zéros en tête)05
SSecondes à deux chiffres (avec zéros en tête)19
LMillisecondes à trois chiffres (avec zéros en tête)047
NNanosecondes à neuf chiffres (avec zéros en tête)047000000
pMarqueur du matin ou de l’après-midipm
zDécalage numérique RFC 822 par rapport au GMT-0800
ZFuseau horairePST
sQuelques secondes depuis 1970-01-01 00:00:00 GMT1078884319
QMillisecondes secondes depuis 1970-01-01 00:00:00 GMT1078884319047

 

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

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é.

LAISSER UN COMMENTAIRE

Please enter your comment!
Please enter your name here