More efficient way to handle user input data validation? - java

In Java, I have two while loops to validate user input and continuously prompt the user if they input the wrong data type. In this program, I only have 2 questions, but I can imagine a scenario where I have more than 10, at which point 10 while loops would be cumbersome code to read and maintain. Is there a more efficient way to check for errors while continuing to prompt the user? My initial thought is to package the while loop and error checks into a separate class function and call it when input is requested.
import java.util.*;
public class IncreaseAge {
public static void main(String args[]){
Scanner userInput = new Scanner(System.in);
boolean validInput = true;
String coolName = "Adam";
int coolAge = 0;
while(validInput){
try{
System.out.print("Hello, what is your first name? ");
coolName = userInput.nextLine();
validInput = false;
}
catch(Exception error){
System.out.println("Invalid input, try again!");
userInput.next();
}
}
validInput = true;
while(validInput){
try{
System.out.print("Hi "+ coolName + "! How old are you?");
coolAge = userInput.nextInt();
validInput = false;
}
catch(Exception error){
System.out.println("Invalid input, try again!");
userInput.next();
}
}
System.out.println("Hello "+ coolName + ", in ten years you will be " + (coolAge+10));
userInput.close();
}
}

Just implement private int getIntegerInput(String prompt) and private String getStringInput(String prompt), each of which will more or less be the same as the two loops you've already coded.
This is a common and frequent way to avoid code repetition - implement "helper" routines to be used in writing your intended functionality.
Even if you don't have repetition to worry about, it's a useful partitioning of code, making it easier to understand - for example, the "get an input" code is clearly distinct from the "process the input" code.
Example:
private String getStringInput(Scanner scanner, String prompt) {
String input = null;
boolean validInput = false;
while (!validInput) {
try {
System.out.print(prompt);
input = scanner.nextLine();
validInput = !input.isEmpty();
}
catch (Exception error) {
System.out.println("Invalid input, try again!");
}
}
return input;
}
Note that I fixed the use of 'validInput' to make sense, and I assume you want to reprompt on an empty input line.
Usage is then like
String coolName = getStringInput(userInput, "What is your first name? ");

Related

try catch with a do while loop

Using my code I am trying to tell the user to enter not to enter a string until the user an integer but while running the program it is infinite.
public static void main(String[] args) {
int age = 1;
Utilisateur utilisateur = new Utilisateur();
Scanner u = new Scanner(System.in);
System.out.println("Enter your Name: ");
utilisateur.setNom(u.nextLine());
System.out.println("Enter your Surname: ");
utilisateur.setPrenom(u.nextLine());
System.out.println("Enter your Matricule: ");
utilisateur.setMatricule(u.nextLine());
System.out.println("Enter your Sexe: ");
utilisateur.setSexe(u.nextLine());
do {
try {
System.out.println("Enter your Age: ");
utilisateur.setAge(u.nextInt());
System.out.println(utilisateur.detail());
age = 2;
} catch (Exception e) {
System.out.println("Enter a valid age ");
}
}
while (age == 1);
}
}
Okay, so let's start by cleaning up the code a bit. The whole "age" variable is a bit weird. It seems like it's containing some status on whether or not you've read the age. But that's kind of boolean, isn't it? So let's redo the code with that in mind. I'll change the do-while to a simple while first, but we can change it back afterwards. Furthermore, it might be a good idea to rename "u" to "keyboard", or "clavier" if you prefer french.
public static void main(String[] args) {
Utilisateur utilisateur = new Utilisateur();
Scanner clavier = new Scanner(System.in);
System.out.println("Enter your Name: ");
utilisateur.setNom(clavier.nextLine());
System.out.println("Enter your Surname: ");
utilisateur.setPrenom(clavier.nextLine());
System.out.println("Enter your Matricule: ");
utilisateur.setMatricule(clavier.nextLine());
System.out.println("Enter your Sexe: ");
utilisateur.setSexe(clavier.nextLine());
boolean hasEnteredAge = false;
while(!hasEnteredAge) {
System.out.println("Enter your Age: ");
String ageInput = clavier.nextLine().trim(); // remove leading and trailing whitespace. " 21 " becomes "21".
try {
int age = Integer.parseInt(ageInput);
utilisateur.setAge(age);
System.out.println(utilisateur);
hasEnteredAge = true;
} catch (Exception e) {
System.out.println("Enter a valid age.");
}
}
}
}
Notice that I moved the variable to the beginning of the loop, which is where we need to know about this fact, and how we initialized it to false. We now have to set it to be true afterwards.
But there is a bit more to do here I think. We have a bunch of prints, followed by inputs. Surely, this can be farmed out to a method, that makes this look a bit nicer? But before we do that, we should take another look at the loop. We can do the loop in a multitude of ways. We can do
do {
System.out.println("Enter your Age: ");
String ageInput = clavier.nextLine().trim(); // remove leading and trailing whitespace. " 21 " becomes "21".
try {
int age = Integer.parseInt(ageInput);
utilisateur.setAge(age);
System.out.println(utilisateur);
break; // this means that we should exit the loop
} catch (Exception e) {
System.out.println("Enter a valid age.");
}
}while(true); // So if we ever get here, we're not done.
Here, we're relying on the break to get us out of the loop. This works, but personally I don't like it. It's not a wrong thing to do however, so I'll just leave it in. You can also have it like the old do-while loop:
boolean hasEnteredAge = false;
do {
System.out.println("Enter your Age: ");
String ageInput = clavier.nextLine().trim(); // remove leading and trailing whitespace. " 21 " becomes "21".
try {
int age = Integer.parseInt(ageInput);
utilisateur.setAge(age);
System.out.println(utilisateur);
hasEnteredAge = true;
} catch (Exception e) {
System.out.println("Enter a valid age.");
}
} while (!hasEnteredAge);
Whichever you choose though, it's fine.
Now let me just tackle the issue of the printlines and reads:
If you add a method "prompt" that takes a prompt and returns a string, you can simplify this down quite handily like so:
public class EnterNameHere {
private static Scanner clavier = new Scanner(System.in);
public static String prompt(String prompt) {
System.out.println(prompt);
return clavier.nextLine().trim();
}
// ... The rest is as before.
}
Now, the reading in part becomes very simple:
public static void main(String[] args) {
Utilisateur utilisateur = new Utilisateur();
utilisateur.setNom(prompt("Enter your Name: "));
utilisateur.setPrenom(prompt("Enter your surname: "));
utilisateur.setMatricule(prompt("Enter your matricule: "));
utilisateur.setSexe(prompt("Enter your sex: "));
And an important question arises: If we are to do this for string inputs, why not for integer (int) inputs as well?
I propose:
public static int promptInt(String prompt) {
String value = prompt(prompt);
try {
return Integer.parseInt(value);
} catch(NumberFormatException ignored) {
System.out.println("Invalid number: '" + value + "'");
return promptInt(prompt); // We try again!
}
}
Notice if you would be so kind, that if calling the method promptInt doesn't work, we print an error message and just try again. This will only work for a few hundred times before it all crashes, but that should be enough. (You can of course adapt the while-loop approach from earlier if you don't want that to happen.) This trick of a method or function calling itself multiple times until the work is done is called "recursion" and it is as powerful as looping is. It can be confusing to people who are new to programming, but I think this example is straightforward. If it isn't, you can simply substitute the whole loop thing as mentioned. Of course, there is one method called prompt, and another called promptInt. To avoid any confusion we rename the prompt-method to promptString, and the entire program simply becomes:
public class YourNameHere {
private static final Scanner clavier = new Scanner(System.in);
public static String promptString(String prompt) {
System.out.print(prompt);
return clavier.nextLine().trim();
}
public static int promptInt(String prompt) {
String value = promptString(prompt);
try {
return Integer.parseInt(value);
} catch(NumberFormatException ignored) {
System.out.println("Invalid number: '" + value + "'");
return promptInt(prompt); // We try again!
}
}
public static void main(String[] args) {
Utilisateur utilisateur = new Utilisateur();
utilisateur.setNom(promptString("Enter your Name: "));
utilisateur.setPrenom(promptString("Enter your surname: "));
utilisateur.setMatricule(promptString("Enter your matricule: "));
utilisateur.setSexe(promptString("Enter your sex: "));
utilisateur.setAge(promptInt("Enter your age: "));
System.out.println("You have created an utilisateur: " + utilisateur);
}
}
Plus the definition of Utilisateur of course.
I think this is a much simpler way to do it, by creating methods that does the boring work for you, you can read the code in the main method and immediately understand what is going on. If you need to understand how, you can go up and look at the helping prompt-methods.
You should add u.nextLine(); in catch block in order to skip invalid value entered in the scanner.

First if conditions not working correctly

When I run my code it works fine until it gets to the point where my if statement evaluates the answer string. It will always run the first if statement no matter what I input into the scanner object. And if I take out the scanner.nextLine(); then it will not let me enter any input for the answer object.
public class ExceptionTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
boolean statement = true;
int total;
String answer = null;
do{
try{
System.out.println("Please enter the amount of Trees:");
int trees = scanner.nextInt();
if(trees < 0){
throw new InvalidNumberException();
}
System.out.printf("Amount of fruit produced is: %d%n", trees * 10);
System.out.println("Please enter the amount of people to feed: ");
int people = scanner.nextInt();
scanner.nextLine();
total = trees * 10 - people * 2;
System.out.printf("The amount of fruit left over is: %d%n", total);
statement = false;
}
catch(InvalidNumberException e){
System.out.println(e.getMessage());
scanner.nextLine();}
}
while(statement);
System.out.println("Would you like to donate the rest of the fruit? Y or N:");
try{
answer = scanner.nextLine();
if(answer == "Y"){
System.out.println("Your a good person.");
}else if(answer == "N"){
System.out.println("Have a nice day.");
}else {
throw new NumberFormatException();
}
}
catch(NumberFormatException e){
System.out.println(e.getMessage());
scanner.nextLine();
}
}
}
There are three things here:
The first call to scanner.nextLine() gets the user input, but isn't stored in a variable. The answer variable is storing a second, unnecessary, call to scanner.nextLine(). EDIT The reason why scanner.nextLine() is needed is the previous scanner call is to scanner.nextInt(). nextInt() doesn't move the cursor to the next line. See: Scanner is skipping nextLine() after using next() or nextFoo()?.
You can't compare Objects using == and !=. Java's comparison operators test for strict object equality. You don't mean to check for whether they are the same String instance, you mean to check if the two instances store the same text. The correct comparison is !answer.equals("Y").
Think about the logic of the if statement. Think about what the || operator means.
I have seen that you edited your answer which was good because the logic was wrong in there. You should check first if the input was Y or N before verifying it is not in the two. Your problem is pretty simple, it is a common problem regarding the scanner class. Just add String trash = scanner.nextLine(); and remove many unnecessary scanner.nextLine();
String trash = scanner.nextLine();
System.out.println("Would you like to donate the rest of the fruit? Y or N:");
try {
answer = scanner.nextLine();
if (answer.equals("Y")) {
System.out.println("Your a good person.");
} else if (answer.equals("N")) {
System.out.println("Have a nice day.");
} else {
throw new NumberFormatException();
}
} catch (NumberFormatException e) {
System.out.println(e.getMessage());
}
Add String trash whenever the Scanner messes up in your case it was near the answer=scanner.nextLine(); If there are still errors just comment. I am happy to help

How to avoid infinite loop when using try/catch?

I have this code
public void askUserForStrategy(){
try{
System.out.println("What strategy do you want to use?\n");
System.out.println("1 = Math.random\t 2 = System time\t "
+ "3 = Sum of Math.random and System time");
int strategy = sc.nextInt();
selectStrategy(strategy);
}
catch(InputMismatchException Exception){
System.out.println("That is not an integer. Try again.\n");
askUserForStrategy();
}
}
What I want it to do is basically ask the user to type in an integer, and in case the user the users types a String for example, catch that exception and start the method again (ask the user to type in an integer value). But the method loops when the user types in a String.
When nextInt() throws an exception, the Scanner object tries to use the same string on the next call.
Try allocating a new Scanner Object within the try. Or try to call nextLine() within the catch, so you will discard the illegal line.
Please note that this method is not good, because after too many illegal inputs (very many, but the ideal is to have infinite tries) a stack overflow will occur.
I suggest you to use a do-while and return at the end of the try body.
Try this:
public void askUserForStrategy() {
for(int i=0; i<1; ++i) {
try{
System.out.println("What strategy do you want to use?\n");
System.out.println("1 = Math.random\t 2 = System time\t "
+ "3 = Sum of Math.random and System time");
int strategy = sc.nextInt();
selectStrategy(strategy);
break; //break loop when there is no error
}
catch(InputMismatchException Exception){
System.out.println("That is not an integer. Try again.\n");
//askUserForStrategy();
continue; //for clarity
}
}
}
Maybe you're looking for something like this
public void askUserForStrategy(){
Boolean loopFlag = true;
while(loopFlag) {
try{
System.out.println("What strategy do you want to use?\n");
System.out.println("1 = Math.random\t 2 = System time\t "
+ "3 = Sum of Math.random and System time");
int strategy = sc.nextInt();
Integer.parseInt(strategy);
loopFlag = false;
selectStrategy(strategy);
}
catch(Exception e){
//Parse has failed due to wrong input value, But loop will continue
}}}
This may be you are looking for..
public void askUserForStrategy() {
while (true) {
try {
Scanner sc = new Scanner(System.in);
System.out.println("What strategy do you want to use?\n");
System.out.println("1 = Math.random\t 2 = System time\t " + "3 = Sum of Math.random and System time");
int strategy = sc.nextInt();
System.out.println("Selected strategy : " +strategy);
break;
} catch (Exception e) {
System.out.println("That is not an integer. Try again.\n");
continue;
}
}
// selectStrategy(strategy);
}
If user selects String, then again it will ask for options..
If user selects Integer, then it will take user selected strategy option and continue the program flow.. (Means come out of the while loop with the help of break and then calling selectStrategy method)
Thanks

Having an incedibly tough time with a loop

I've copied part of the instructions below, and I can code pretty much every part on its own, but getting the control flow together is giving me massive doubts about my ability.
One of my biggest problems is the int gameChanger. Im supposed to immediately verify if it is a integer or not, and loop back if its not. But then Im also supposed to check to see if thebuser ever types "exit". But the input variable for my scanner instance is an integer... So Im stumped. I can use a try catch to check the missmatchexception once the input is being read in, but that doesnt solve the exit issue nor am I able to come up with solid logic to get the try catch to loop back if it indeed isnt an integer. Im thinking a do while loop but I havent gotten it to work.
Instructions:
You can whether the input is a number before attempting to consume it.
int num;
while (true) {
if (scanner.hasNextInt()) {
num = scanner.nextInt();
break;
} else {
// read whatever is there instead.
String line = scanner.nextLine();
if (line.equals("exit"))
System.exit(0);
System.out.println("Please enter a number");
}
}
System.out.println("Number entered " + num);
This gets the job done. Try it out.
import java.util.Scanner;
public class MyCode
{
public static void main(String[] args)
{
String gameInput = ".";
int gameNumber = 0;
boolean inputLoop = true;
Scanner input = new Scanner(System.in);
while(inputLoop == true)
{
try
{
System.out.print("Please enter a valid game number: ");
gameInput = input.next();
if(gameInput.equals("exit"))
{
System.out.println("Program will now end. Goodbye.");
inputLoop = false;
input.close();
}
gameNumber = Integer.parseInt(gameInput);
if(gameNumber >= 20001 && gameNumber <= 21230)
{
System.out.println("You have inputted a valid game number.");
inputLoop = false;
input.close();
}
}
catch(NumberFormatException e)
{
if(!gameInput.equals("exit"))
{
System.err.println("Invalid game number. Please try again.");
}
}
}
}
}

Scanner loop doesn't cycle when hitting enter twice

boolean loop = false;
double numberOfStudents;
System.out.print("Enter a number: ");
if ((scnr.nextLine().trim().isEmpty()) ) {
loop = true;
}
while (loop) {
System.out.println("Enter a number");
if (scnr.hasNextDouble() ){
System.out.println("Loop has stopped");
numberOfStudents = scnr.nextDouble();
loop = false;
}
}
System.out.println("You're outside the loop!");
I'm trying to get the program to say "Enter a number" until the user has entered an actual number (no white spaces or letters or signs). When the user has entered a number, it sets numberOfStudents equal to that number and breaks out of the loop.
But if you hit enter twice, it doesn't iterate. It only displays "Enter a number" once.
What is wrong with the loop logic? Why isn't it looping until valid input is taken?
For the actual answer to your question of "Why doesn't 'Enter a number' display more than once?" see Tom's comment (update: Tom's answer).
I've rewritten your loop in a way which preserves your code, but also makes it a little easier to handle format exceptions (though at the risk of silently swallowing an exception -- should be acceptable for this use case).
Can be up to you to use this design, here is an SO post on why empty catch blocks can be a bad practice.
public static void main(String args[])
{
boolean loop = true;
double numberOfStudents;
Scanner scnr = new Scanner(System.in);
while(loop){
System.out.print("Enter a number: ");
String input = scnr.nextLine();
try{
numberOfStudents = Double.parseDouble(input);
loop = false;
}catch(NumberFormatException e){
}
}
System.out.println("You're outside the loop!");
}
Output:
Enter a number:
Enter a number:
Enter a number:
Enter a number: 50
You're outside the loop!
First of all: Since you're reading from System.in a call to the input stream will block until the user entered a valid token.
So let's check first scan using your scnr variable:
scnr.nextLine()
nextLine() reads everything til the next line delimiter. So if you just press return, then it will successfully read it and will perform the next stuff.
The next call is:
scnr.hasNextDouble()
This call expects a "real" token and ignores white spaces, except as a delimiter between tokens. So if you just press return again it doesn't actually read that input. So it still waits for more (for the first token). That is why it stucks in your loop and you won't get another "Enter a number" output.
You can fix that by either enter a real token, like a number, or by changing the loop like trobbins said.
I hope you now understand your program flow a bit more :).
While trobbins code basically solves your problem, it's bad practice to use exceptions for flow control.
I used a small regexp to check if the value is a number. But this example is not complete, it will still crash it the user enters for example two decimal points. So you would need to create a proper number check or just use integers where the check is much easier.
Someone in the comments pointed out that people may want to enter scientific notation like 5e10, so this would also be another case to check for. If this is just some code you need as a proof of concept or something quick and dirty, you can go with the exception handling method but in production code you should avoid using exceptions this way.
double numberOfStudents;
Scanner scnr = new Scanner(System.in);
while(true) {
System.out.print("Enter a number: ");
String input = scnr.nextLine().trim();
if(input.matches("^[0-9\\.]{1,}$")) {
System.out.println("Loop has stopped");
numberOfStudents = Double.parseDouble(input);
break;
}
}
System.out.println("You're outside the loop!");
The following code should help you:
double numberOfStudents = 0;
Scanner scnr = new Scanner(System.in);
boolean readValue = false; //Check if the valid input is received
boolean shouldAskForNumber = true; //Need to ask for number again? Case for Enter
do {
if (shouldAskForNumber) {
System.out.print("Enter a number:");
shouldAskForNumber = false;
}
if (scnr.hasNextDouble()) {
numberOfStudents = scnr.nextDouble();
readValue = true;
} else {
String token = scnr.next();
if (!"".equals(token.trim())) { //Check for Enter or space
shouldAskForNumber = true;
}
}
} while (!readValue);
System.out.printf("Value read is %.0f\n", numberOfStudents);
System.out.println("You're outside the loop!");
Update
Understood the following statement in question different way:
But if you hit enter twice, it doesn't loop back. It only displays
"Enter a number" once.
The code is set to print "Enter a number" only once if the user hits RETURN/ENTER or enters space character. You may remove the special check and use the code if needed.
import java.util.Scanner;
public class Testing {
public static boolean checkInt(String s)
{
try
{
Integer.parseInt(s);
return true;
} catch (NumberFormatException ex)
{
return false;
}
}
public static void main(String[] args) {
boolean loop = false;
double numberOfStudents;
Scanner scnr = new Scanner(System.in);
String input = "";
while (!(checkInt(input))) {
System.out.println("Enter a number");
input = scnr.nextLine();
}
numberOfStudents = Integer.parseInt(input);
System.out.println("Number of students: " + numberOfStudents );
}
}
//this code is working fine, if you want you check it out.
//In your code your taking another input if the first is an int/double; if the first input is not a number then you have mentioned to take input again..
Use a debugger to see what the code is actually doing. Here's a guide on debugging in Eclipse. After you have finished debugging your code, you will probably know what the problem is.
Below code will help you
boolean loop = true;
double numberOfStudents;
Scanner scnr = new Scanner(System.in);
System.out.print("Enter a number: ");
String input = scnr.nextLine();
while(!scnr.hasNextDouble()){
System.out.print("Enter a number: ");
try{
numberOfStudents = Double.parseDouble(input);
break;
}catch(NumberFormatException e){
}
input = scnr.nextLine();
}
System.out.println("You're outside the loop!");
The following code is working,
boolean loop = true;
double numberOfStudents;
Scanner scnr=new Scanner(System.in);
while(loop) {
System.out.println("Enter a number");
if ((scnr.nextLine().trim().isEmpty()) ) {
loop = true;
}
if (scnr.hasNextDouble() ){
System.out.println("Loop has stopped");
numberOfStudents = scnr.nextDouble();
loop = false;
}
}
System.out.println("You're outside the loop!");
The output is,
run:
Enter a number
hj
po
Enter a number
lhf
Enter a number
o
Enter a number
p
Enter a number
a
Enter a number
34
Loop has stopped
You're outside the loop!
You have to scan the next line if you want to get more values form the scanner again. The code should be like:
while (loop) {
System.out.println("Enter a number");
if(!(scnr.nextLine().trim().isEmpty())){
if (scnr.hasNextDouble() ){
System.out.println("Loop has stopped");
numberOfStudents = scnr.nextDouble();
loop = false;
}
}
}

Categories

Resources