My program is supposed to accept a floating-point variable and then exit. However I am practicing some exception handling stuff and found a problem. Whenever you enter a letter into this program the program of course throws an InputMismatchException but it gets stuck inside an infinite loop. I assume my problem is based off of my misunderstanding of try-catch statements and exception handling.
public static void main(String [] args){
Scanner reader = new Scanner(System.in);
boolean done = false;
do{
try{
System.out.print("Enter a number: ");
float number = reader.nextFloat();
done = true;
}
catch (Exception e){
System.out.println("uh oh");
}
}while(!done);
}
This problem does not occur if I use a different variable type so I'm not sure if it's a logical error or just something funky with floating-point variables.
Float#nextFloat() does not consume the token in the Scanner if it throws an InputMismatchException. So when you get the exception and loop (because done is still false), you try to call nextFloat() again. Since the token is still not a value that can parsed into a float, the Scanner again throws the exception. And again, and again, ad nauseam.
You should use hasNextFloat() to check for the existence of a token that can be parsed to a float value. Or consume the incorrect value with Scanner#next(), as suggested by Quirliom.
Related
This question serves as a follow-up to a previous question.
I am trying to create a program that converts from one currency to another.
First, I'll post and explain the relevant code, then describe the desired outcome and some problems I came across.
do {
try {
invalidInput = false;
String line = input.nextLine();
Scanner lineScan = new Scanner(line);
BigDecimal moneyInput = lineScan.nextBigDecimal();
// second do-while loop that tries to emulate the outermost do-while loop
// introduces a second set of nested scanners
// re-prompts user for valid currency if they try a blank input
do {
try {
String line2 = input.nextLine();
Scanner lineScan2 = new Scanner(line2);
String currency = lineScan2.next();
if (currency.equals("USD")) {
// convert USD to CNY
} else if (currency.equals("CNY")) {
// convert CNY to USD
}
} catch (NoSuchElementException e) {
invalidInput = true;
System.err.print("Please enter a valid CURRENCY: "); // prompts user for currency ONLY
}
} while (invalidInput);
} catch (NoSuchElementException e) {
invalidInput = true;
System.err.print("Please enter the VALUE followed by the CURRENCY: ");
}
} while (invalidInput);
The outer do-while loop runs as long as invalidInput is true. At the beginning of the first try block, invalidInput is false, and it remains false unless the user enters an invalid input (e.g. blank input or non-numeric). Here, invalidInput becomes true, and the program loops back to the top of the try block after re-prompting the user for both moneyInput and currency.
Next, I wanted to find someway to check the validity of moneyInput and currency separately. The first set of nested Scanners is meant to process moneyInput. If the user inputs something invalid, it will re-prompt the user Please enter the VALUE followed by the CURRENCY:, and it will continue to do so until something valid is entered.
I then wanted to add the exact same functionality to check currency exclusively. This is the intended purpose of the second set of nested scanners (lineScan2). If a valid input is entered for moneyInput but not for currency, e.g. 100.00 abc, I'd like the program to retain the value of moneyInput, re-prompt the user for only currency, and continue to do so until a valid currency is entered (including if the user enters a blank input).
Here are some problems I'm running into:
The program is only reading moneyInput in the first line, instead of reading both moneyInput and currency. Secondly, for each input, the user must press [return] twice (note each of the empty lines in between each input).
The program also terminates inconsistently. In the image example above, after it finally accepts a valid moneyInput and currency and converts the value, the program does not terminate. It terminates prematurely if the moneyInput is entered first, followed by an invalid currency on a second line, like so:
But here, it terminates properly after a successful run (although this still isn't exactly right because it only is "successful" if moneyInput and currency are input on separate lines; ideally, the user should be able to enter both on the same line and the program prints the appropriate conversion):
However, one thing the program does do well is responding repeatedly to invalid (specifically, blank inputs):
And actually, in the case above, aside from the fact that [return] had to be entered twice when prompting for moneyInput and that it didn't terminate after a successful run, everything is working exactly as desired:
the user gets to try repeatedly until a valid input, and in the case
where moneyInput is valid but currency is not, the user only has
to enter a valid input for currency (i.e. they don't have to
re-enter moneyInput when only the currency was invalid).
So overall I am looking for ways to modify the code above to achieve the desired results. As mentioned in the comment section of the linked question at the very top of this post, another method I tried was another do-while loop inside (in place of the inner do-while loop) to check currency, and this worked except for when a blank input was entered, which is basically problem I had at the beginning of that post (here is my code: pastebin.com/raw/CT0qjBPk and example screenshots: imgur.com/a/mjfaL).
Sorry if this post is excessively specific and lengthy. I am a beginner programmer and trying to study Java as thoroughly as possible, and this process of improving code has been of great educational value. Thanks so much for reading and providing feedback.
Your implementation is overly-complex because you're using input in several different places. Here's essentially the pattern I suggested in my answer to your previous question:
try (Scanner in = new Scanner(System.in)) {
while (in.hasNextLine()) {
String line = in.nextLine();
doSomethingWithALineOfInput(line);
}
}
Here's roughly what your code is doing:
boolean invalidInput = false;
try (Scanner in = new Scanner(System.in)) {
while (in.hasNextLine()) {
do {
String line = input.nextLine();
invalidInput |= doSomethingWithALineOfInput(line);
do {
String line2 = input.nextLine();
invalidInput |= doSomethingWithASecondLineOfInput(line2);
} while (invalidInput);
} while (invalidInput);
}
}
Notice in particular that you're calling input.nextLine() in two separate places, which makes your code hard to reason about. One of the primary goals when programming is to reduce your problem into smaller subproblems - interleaving input.nextLine() calls everywhere (let alone inside nested do-while loops) forces you to continue dealing with one big problem.
So instead of mixing your line-processing and your token-processing code together, isolate them from each other. The only thing that should interact with input is the very outer while loop. Once you've read a line you're done with input for the remainder of that iteration.
Consider something like this (notice the use of a class to contain the values as we read them in):
class PromptForMoney {
private BigDecimal amount;
private String currency;
public void prompt(Scanner in) {
System.out.print("Enter an amount of money and currency to convert: ");
while (in.hasNextLine()) {
try {
processLine(in.nextLine());
return;
} catch (InputMismatchException | NoSuchElementException e) {
// we use the exception message to describe the problem to the user
// if Scanner generates exceptions with unclear messages you can
// catch them in processLine() and throw your own with a better message.
System.out.print("Invalid input - " + e.getMessage() + ": ");
}
}
throw new NoSuchElementException(
"No more input to read, but a valid amount or currency was not entered.");
}
private void processLine(String line) {
Scanner lineScanner = new Scanner(line);
if (amount == null) {
// this line will raise an exception if the line is empty
// or if it doesn't start with numerical token
amount = lineScanner.nextBigDecimal();
}
if (currency == null) {
// this line will raise an exception if the user didn't specify a currency
String c = lineScanner.next();
if (isValidCurrency(c)) {
currency = c;
} else {
throw new InputMismatchException(c + " is not a valid currency");
}
}
// if we get this far without raising an exception we've read a valid
// amount and currency from the user.
}
}
Notice how prompt() deals solely with lines, and processLine() deals solely with the contents of a single line. This cleanly separates the problem into smaller, easier-to-reason-about parts.
You'd use PromptForMoney like so:
public static void main(String[] args) throws IOException {
PromptForMoney prompt = new PromptForMoney();
try (Scanner in = new Scanner(System.in)) {
prompt.prompt(in);
}
System.out.println(convert(prompt.getAmount(), prompt.getCurrency());
}
There's another separation of concerns - only main() is responsible for directly interacting with System.in. As far as PromptForMoney is concerned its Scanner could be backed by a string or a file and it would work exactly the same.
Caveat: there are some best practices I'm not emphasizing for the sake of space and simplicity (e.g. preferring final instance variables). If you're interested in improving your code quality even further I strongly suggest reading Effective Java which goes into great detail about Java design patterns.
This question already has answers here:
Endless while loop problem with try/catch
(2 answers)
Closed 7 years ago.
Below code,
import java.util.Scanner;
public class Dummy {
static Scanner sc = new Scanner(System.in);
public static int getIntegerInput(String prompt){
int choice = 0;
for(;;){
System.out.print(prompt);
try{
choice = sc.nextInt();
break;
}catch(java.util.InputMismatchException ex){
System.out.print("What??? ");
}
}
return choice;
}
public static void main(String[] args) {
int choice = getIntegerInput("Enter a number: ");
} //end main
}
does not stop for next user input, if the first user input raised an exception.
How do I understand this problem in the above code? placing sc.next() in catch resolves the problem. But I'm still not clear what is going on under the hood? What is the right approach to resolve this problem?
When nextXYZ() fails to consume a token it leaves it in the InputStream. Here, you are looping over the same input endlessly - each iteration, you attempt to consume this token, throw an exception if it isn't an integer, catch it, and try reading it again - forever.
EDIT:
In order to work around this, you could use next() to consume that token and move on to the next one:
for(;;) {
System.out.print(prompt);
try{
choice = sc.nextInt();
break;
} catch(java.util.InputMismatchException ex) {
sc.next(); // here
}
}
The problem with Scanner next() are they will not advances if the match is not found. And the character for which it failed remain in the stream. Hence its very important to advance the scanner if you found non intended character.
You can use next() method which actually consumes any character or you can use skip method passing skip pattern.
Use hasNext() to know whether a valid match is present or not. If not then consume that character using above said methods.
If it doesnt find an int on the next like, it throws an error. This error is then caught by your program, so the break is never hit because the error jumps over it whenever a non-int (including nothing) is found.
When I run th code, and say input 2.5, the output I get is:
2.5, 4 times "error", and 5.
It means that the computer goes through the catch statement every time, instead of asking for input every time it loops through the while loop and enters the try block.
public static void main(String[] args)
{
Scanner s1 = new Scanner(System.in);
int n = 0;
while(n<5)
{
try
{
int grade = s1.nextInt();
System.out.println(grade);
}
catch(Exception e)
{
System.out.println("error");
}
n++;
}
System.out.println(n);
}
When you enter "2.5", nextInt() consumes the 2. The next thing being scanned by the very same nextInt() will be . and that cannot be successfully scanned by nextInt(), so you get the error. nextInt() can only be used to scan int numbers, if you want to scan fractions like 2.5, you need nextDouble().
By the way, the exception objects hold useful information. If you do this, you're just hiding the error information:
catch (Exception e) {
System.err.println(error):
}
Instead do this:
catch (Exception e) {
e.printStackTrace();
}
And don't mix System.out and System.err. System.out is for the normal program output but not for logging, debug, info, warning, error or such messages. Those should go to System.err.
Basically, there is a "cursor" that points to the next character of your input line that a Scanner will read. If the input line looks like
bad input
the cursor starts before the b. If you call nextInt(), the Scanner will throw an exception, because b can't be the start of an integer. But the cursor stays in the same place. So next time you call nextInt(), the same error will happen all over again.
You need to do something that will "consume" the bad input so that the cursor will move. s1.nextLine(), as in almas' answer, will consume the entire rest of the line.
If the input is
2.5
nextInt() will throw an exception because 2.5 is not an integer. Once again, the cursor stays in the same place, so the error will keep coming up, unless you do something to consume the 2.5. If you want the program to actually read 2.5, then don't use nextInt() since 2.5 is not an integer.
According to the documentation:
This method will throw InputMismatchException if the next token cannot be translated into a valid int value as described below. If the translation is successful, the scanner advances past the input that matched.
This means that if the translation is NOT successful, the scanner does not advance past the input. It keeps trying to translate .5 in every iteration.
You can get the result I assume you expect by adding s1.next() in your catch statement to clear the token in the Scanner:
public static void main(String[] args)
{
Scanner s1 = new Scanner(System.in);
int n = 0;
while(n<5)
{
try
{
int grade = s1.nextInt();
System.out.println(grade);
}
catch(Exception e)
{
**s1.next();**
System.out.println("error");
}
n++;
}
System.out.println(n);
}
And as many have already mentioned, output all possible information from exception!
This question already has answers here:
try/catch with InputMismatchException creates infinite loop [duplicate]
(7 answers)
Closed 7 years ago.
I keep getting my code caught in an infinite while loop.
It is nothing to advanced, but i can not figure it out for the life of me!
Someone Please help
I have purplosely just re created the specific error without all of the if statements i have in my actual program.
package bs;
import java.util.InputMismatchException;
import java.util.Scanner;
public class bs {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
boolean continueVar = true;
while (continueVar) {
try {
System.out.println("Enter Something");
int input = sc.nextInt();
} catch (InputMismatchException i) {
System.out.println("What the f***?");
continueVar = true;
}
}
}
}
The infinite loop occurs when the Input mismatch exception is caught. I would think that it would atleast ask the user to re enter their input but instead of doing that it just continues in the loop as so:
run:
Enter Something
df
What the f***?
Enter Something
What the f***?
Enter Something
What the f***?
It acts like it is just ignoring the scanner object sc?!
No the scanner is not skipped, it's just starting at the beginning of the input. From the JavaDoc:
If the translation is successful, the scanner advances past the input that matched.
This means if the conversion isn't successfull the scanner won't advance. You'd thus have to manually skip the incorrect input using just next().
Edit: you might want to check for hasNextInt() before trying to read the input.
you loop while continueVar is true, but you never set to to false, so the loop never exits.
I think you want to set continueVar to false in the exception handler.
When a scanner throws an
InputMismatchException, the scanner
will not pass the token that caused
the exception, so that it may be
retrieved or skipped via some other
method.
The token that caused the mismatch is still in the scanner's buffer. You need to clear it before trying to scan again.
You can do that by calling next() in your catch block, like this:
catch (InputMismatchException i) {
System.out.println("What the f***?");
sc.next();
}
Also, you don't need to set continueVar to true again. You never set it to false, so it will stay true. Guessing that's an artifact of you removing this into a mini program.
Scanner does not advance when bad token is found. Look at Scanner.java, lines 2095-2096:
catch (NumberFormatException nfe) {
position = matcher.start(); // don't skip bad token
import java.util.*;
public class June16{
public static void main(String[] args){
Scanner kb = new Scanner(System.in);
double b=0;
boolean checkInput = true;
do{
try{
System.out.println("Input b : ");
b = kb.nextDouble();
checkInput = false;
}catch(InputMismatchException ime){
}
}while(checkInput);
}
}
After InputMismatchException is thrown, why my program not prompt for input? :D
From the documentation:
When a scanner throws an InputMismatchException, the scanner will not pass the token that caused the exception, so that it may be retrieved or skipped via some other method.
This is why you end up in an infinite loop if you don't enter a valid double. When you handle the exception, move to the next token with kb.next().
Because if the Scanner.nextDouble() failes it leaves the token on the queue, (which is then read again and again causing it to fail over and over again).
Try the following:
try {
// ...
} catch (InputMismatchException ime) {
kb.next(); // eat the malformed token.
}
ideone.com demo illustrating working example
This is due to the fact that nextDouble will take the decimal number you entered, but there is still a carriage return that you enter that was not read by the scanner. The next time it loops it reads the input, but wait! there is a carriage return there, so... no need to scan anything. It just processes the carriage return. Of course, the program finds that it is not a double, so you get an exception.
How do you fix it? Well, have something that scans whatever leftovers were left by the nextDouble (namely a next()) and then scan the next double again.