Java >> Tutoriel Java >  >> Java

Soyez prudent avec les méthodes de scanner en Java

Pour les nouveaux étudiants, il est souvent amusant d'écrire des programmes interactifs en utilisant Scanner en Java. Malheureusement, il existe une poignée de pièges désagréables qui ne contribuent pas vraiment à une expérience positive pour ces étudiants. Par conséquent, je suis venu avec un avertissement :soyez prudent avec les méthodes Scanner en Java.

Le problème rencontré par les étudiants

Quand j'ai appris Java pour la première fois, je n'ai jamais utilisé Scanner. En fait, je n'avais même pas touché à l'utilitaire avant d'avoir déjà deux ans d'expérience dans l'industrie à mon actif. Mais pour une raison quelconque, le programme Java de mon établissement actuel utilise beaucoup Scanner, alors j'ai pensé que je parlerais un peu des pièges de certaines des méthodes Scanners.

En particulier, je veux parler de l'utilisation de Scanner pour lire les entrées de l'utilisateur à partir de la ligne de commande. Souvent, cela est souhaitable car nous voulons inviter un utilisateur à entrer une entrée comme son nom ou un numéro favori. Ensuite, nous effectuons des calculs amusants et transmettons les résultats à l'utilisateur. Je vais vous donner un exemple :

Enter your name: Jeremy
Hi, Jeremy!

Dans cet exemple, nous avons demandé à l'utilisateur d'entrer son nom et nous le lui avons renvoyé. Nous pouvons le faire en utilisant l'extrait de code Java suivant :

Scanner input = new Scanner(System.in);
System.out.print("Enter your name: ");
String name = input.nextLine();
System.out.println("Hi, " + name + "!");

Ce n'est pas si mal! Mais que se passe-t-il si nous voulons demander un numéro ? Heureusement pour nous, l'API Scanner dispose de toute une série de méthodes de création de jetons comme nextLine() pour récupérer tout ce que nous voulons de l'utilisateur et le convertir automatiquement dans le type approprié. Cela dit, soyez prudent lorsque vous les utilisez.

L'explication souhaitée par les étudiants

À ce stade, de nombreux étudiants me demanderont :

Quel est le problème ? Pourquoi devrais-je appeler nextLine() et analyser la valeur à la main alors que je peux simplement appeler directement la méthode appropriée ?

Étudiants partout

Et, leur inquiétude est tout à fait valable ! Je ne demanderais jamais à personne de faire plus de travail que nécessaire, alors quel est le problème ?

Problèmes

Eh bien, il s'avère que si vous commencez à utiliser des méthodes comme nextLine() sans vraiment prêter attention à ce que vous analysez, vous rencontrerez quelques problèmes.

Le problème le plus évident est peut-être les problèmes de frappe. Par exemple, disons que nous demandons un numéro à l'utilisateur et qu'il nous donne un nom. Sans une gestion appropriée des erreurs, notre solution va sûrement planter. Cela dit, ce genre de problème n'est pas trop difficile à découvrir ou à résoudre. Après tout, dès que l'utilisateur entre un texte non valide, la solution se bloque avec une erreur utile.

Au lieu de cela, il y a un bien plus infâme problème que même les programmeurs chevronnés conviendront qu'il est difficile à résoudre, et il apparaît lorsque nous commençons à mélanger l'utilisation des différentes méthodes de scanner. Jetez un œil à l'exemple suivant et voyez si vous pouvez comprendre ce qui se passe lorsque je fournis au programme le numéro 25 et mon nom.

Scanner input = new Scanner(System.in);
System.out.print("Enter your age: ");
int age = input.nextInt();
System.out.print("Enter your name: ");
String name = input.nextLine();
System.out.println("Your name is " + name + " and you are " + age);

Sans trop creuser dans la solution, la personne moyenne dirait que cette solution imprime "Votre nom est Jeremy et vous avez 25 ans". Et, pour être honnête, je ne leur en veux pas du tout. La logique est là, mais les résultats ne le sont pas. Heureusement, je ne vous laisserai pas tomber !

Chasse aux bogues

Si vous avez déjà pris les devants et testé cette solution vous-même, vous aurez remarqué que quelque chose d'étrange se produit. Pour commencer, notre sortie est fausse. Au lieu de "Votre nom est Jeremy et vous avez 25 ans", la sortie indiquera "Votre nom est et vous avez 25 ans". Tout de suite, nous remarquerons que le nom est absent de la sortie, ce qui signifie que la variable de nom stocke probablement une chaîne vide.

Pour aggraver les choses, lorsque nous exécuterons réellement la solution, nous verrons que nous ne serons jamais invités à entrer qu'une seule fois. Après avoir entré notre âge, le script entier sera affiché à l'écran comme suit :

Enter your age: 25
Enter your name:
Your name is  and you are 25

À ce stade, nous allons nous gratter la tête en nous demandant comment un appel à nextLine() juste immédiatement renvoyé une chaîne vide. Heureusement, j'ai la réponse !

Séquences d'échappement

Chaque fois que nous invitons un utilisateur à entrer, il obtient cette ligne sophistiquée où il peut vider du texte et appuyer sur Entrée . Gardez un œil sur cette dernière partie car elle est cruciale.

Ce texte est composé de caractères qui ont tous une valeur numérique sous-jacente. Par exemple, la lettre « a » a la valeur numérique 97 tandis que le nombre « 7 » a la valeur numérique 55, ce qui est assez étrange. Heureusement, il n'est pas nécessaire de mémoriser toutes ces valeurs. Au lieu de cela, consultez une table ASCII pour obtenir une liste des 256 premiers caractères.

Si nous parcourons ce tableau, nous remarquerons qu'il y a une poignée de caractères un peu étranges. Par exemple, toute la première colonne de ce tableau contient une liste de séquences d'échappement. Ces caractères ont un but particulier qui n'est pas toujours clairement visible à l'écran.

Il s'avère que quelques-unes de ces séquences d'échappement sont responsables de la désignation des fins de ligne dans le texte :10 (saut de ligne) et 13 (retour chariot). Selon le système, n'importe quelle combinaison de ces deux séquences d'échappement peut être utilisée pour marquer la fin d'une ligne.

Nouvelles lignes voyous

Pour les besoins de la discussion, nous supposerons que notre système marque les sauts de ligne avec le caractère de saut de ligne. En Java, le caractère de saut de ligne peut être écrit en utilisant '\n'. En d'autres termes, chaque fois que nous appuyons sur la touche Entrée, nous pouvons imaginer l'un de ces caractères se faufiler dans notre texte.

Maintenant que nous connaissons les séquences d'échappement, pourquoi ne pas essayer une autre fois de chasser les bogues ? J'ai mis en surbrillance la ligne où le bogue commence à se manifester.

Scanner input = new Scanner(System.in);
System.out.print("Enter your age: ");
int age = input.nextInt();
System.out.print("Enter your name: ");
String name = input.nextLine();
System.out.println("Your name is " + name + " and you are " + age);

Avez-vous trouvé où le bug pourrait être? Je vais vous donner un indice. Le nextInt() méthode ne se soucie que de saisir le nombre suivant. En d'autres termes, il laisse derrière lui notre caractère de nouvelle ligne malveillant ('\n').

Après avoir appelé nextInt() , nous invitons l'utilisateur à saisir son nom. À ce stade, nous appelons nextLine() qui court jusqu'au prochain caractère de nouvelle ligne. Malheureusement, nous en avons oublié un lorsque nous avons appelé nextInt() . En conséquence, nextLine() n'a pas besoin d'attendre que nous saisissions du texte. Il renvoie simplement une chaîne vide.

Si cela n'est pas clair, je vous recommande d'essayer d'alimenter cet extrait de code au-dessus d'une liste d'âges séparés par des espaces. Par exemple, consultez la transcription suivante :

Enter your age:  20 19 31 15
Enter your name:
Your name is  19 31 15 and you are 20

Comme nous pouvons le voir, l'âge est défini sur le premier numéro de la liste (dans ce cas, "20"), et le programme ne demande jamais le nom. Au lieu de cela, le reste du tampon d'entrée (dans ce cas « 19 31 15\n ») est lu sous forme de ligne et enregistré sous le nom.

Dans la section suivante, nous parlerons de la façon de gérer la nouvelle ligne qui reste.

Tâche de correctif

Heureusement, il existe quelques solutions à ce problème. L'un repose sur la détection de notre erreur et l'autre sur la résolution de la cause première de notre problème.

Puisque nous savons que nous avons laissé derrière nous une nouvelle ligne, tout ce que nous avons à faire est de la consommer. En d'autres termes, nous pouvons faire un appel vide à nextLine() avant de l'appeler comme d'habitude :

Scanner input = new Scanner(System.in);
System.out.print("Enter your age: ");
int age = input.nextInt();
System.out.print("Enter your name: ");
input.nextLine();
String name = input.nextLine();
System.out.println("Your name is " + name + " and you are " + age);

Remarquez comment nous avons ajouté un appel à nextLine() juste au-dessus du point où nous stockons le nom. Maintenant, lorsque nous laisserons derrière nous ce caractère "\n", nous nettoierons notre gâchis à la ligne 5. Ensuite, nous pourrons continuer notre vie à la ligne 6.

Alternativement, nous pouvons supprimer complètement ce problème en remplaçant notre nextInt() call avec un appel à nextLine() . Ensuite, nous devrons analyser notre chaîne pour obtenir l'entier que nous voulons :

Scanner input = new Scanner(System.in);
System.out.print("Enter your age: ");
int age = Integer.parseInt(input.nextLine());
System.out.print("Enter your name: ");
String name = input.nextLine();
System.out.println("Your name is " + name + " and you are " + age);

Au lieu de nettoyer après nous-mêmes, nous pouvons consommer toute la ligne en une seule fois et l'analyser à la main. Personnellement, je préfère cette option, mais les deux fonctionnent très bien. Dans les deux cas, nous avons complètement éliminé ce méchant bogue du scanner.

Ouvrir le forum

Donc, la morale de l'histoire est d'être prudent avec l'API Scanner. Bien que de nombreuses méthodes soient là pour votre commodité, les mélanger peut entraîner des bogues désagréables difficiles à tracer. Bien sûr, c'est à vous de décider ce que vous faites de ces nouvelles connaissances.

En tout cas, c'est tout pour le moment ! Si vous avez aimé cet article ou si vous avez des commentaires, faites-le moi savoir dans les commentaires. De plus, si vous avez des conseils ou des astuces pour gérer Scanner en Java, n'hésitez pas à les partager.


Balise Java