Unexpected infinite loop after input reading - java

I'm trying to create a program that keeps asking a question until the user presses enter. But for some reason, the program goes into an infinite loop that continuously outputs:
"Enter the operation: You need to add '[' at the beginning of the set."
If you accidentally do not follow the rules. Now, the program should print that message but only once. I think the loop continuously asks for the operation but it goes straight to the error (If you do not enter anything, I guess that counts as not using an [ at the beginning).
I already know how to solve it but because I tried everything!! I am not sure how my solution changes anything... Can you give me advice on how to solve it and/or explain to me what is it about the do-while loop implemented at the end that does the trick?
Edit: TextIO is a class written by Eck, D. J. in his book. I will link the chapter where it gives the code here: http://math.hws.edu/javanotes/c2/s6.html
The problematic code is
while (true) {
System.out.print("\nEnter the operation: ");
TextIO.skipBlanks();
if (TextIO.peek() == '\n') { //There is no operation, end the program.
break;
}
try {
calculation();
}
catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
The solution I implemented looks like this:
while (true) {
System.out.print("\nEnter the operation: ");
TextIO.skipBlanks();
if (TextIO.peek() == '\n') { //There is no operation, end the program.
break;
}
try {
calculation();
}
catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
do {
ch = TextIO.getAnyChar(); // Read ahead the "enter".
} while (ch != '\n'); //If necessary, make sure to stop the error message and keep the loop going.
}
Thanks, guys!!

It sounds like you're using TextIO class from David J. Eck's Introduction to Programming Using Java, Eighth Edition book.
According to the source of the class:
[TextIO.peek()] returns the next character in the current input source, without actually removing that character from the input.
You're checking the input actually without removing it. That's why in the next iteration, the loop keeps reading the same bad input, and throwing the exception indefinitely.
Also your fix performs what is lacking after calling peek(). Removing the char from the input.
Hence, replacing peek() by getAnyChar() in your first attempt solves the problem.

I'm not sure what's TextIO, you should use the standard Scanner class.

Related

Does anyone know what's happening with infinite loops and StackOverflow error while using recursive call?

This is a part of a program I'm making for practice, its purpose doesn't really matter.
I have declared the Scanner as a field of the class** (it's not written here sorry).
I want to make a method that returns an answer specified to the users input. And I want to make all the neccesary checks needed so the user can't input a character or symbol, just an integer. When the input data is of the wrong type I want the user to try input again.
public static void Answer() {
System.out.println("\n\t1.It was good! \n\t2.Kinda bad too...");
System.out.println();
if (scanner.hasNextInt()) {
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.println("Oh great!");
break;
case 2:
System.out.println("I see you as well");
break;
default:
System.out.println("Please select a valid answer :");
Answer();
break;
} else {
System.out.println("Please select a number, characters are not acceptable!");
Answer();
}
When I enter an invalid number like 3,4 etc the algorithm triggers the switch block and the recursive call as well, working just fine!
BUT when I enter a character it triggers an infinite recursive call without letting me enter new input form the scanner and ends up in StackOverflow error.
You check if the Scanner has an int. If it does not, then it goes to the else branch.
In the else branch, it calls Answer() again. But here's the catch: your scanner still doesn't have an int. So scanner.hasNextInt() returns false. hasNextInt() does not try to read anything - it merely checks if there is input that can be interpreted as an int.
(Source: Java documentation).
So the program goes again to the else branch, without ever reaching the point where it can try to read input... and there it calls Answer() again. Now, the code still doesn't have an int... so once again, scanner.hasNextInt() returns false, and it goes to the else branch. Once again, it does not get to the part where the scanner consumes input, but instead gets to the part where it calls Answer() again...
And this will continue, until the call stack is full and you get the StackOverflow error.
You'll have to take care of the situation that the user enters something that is not an int. You'll have to consume that input that is not an int.
As an aside, I'd prefer to do this using iteration, rather than recursion.

Java - Scanner: does hasNext() changes accordingly after setting useDelimiter()?

Code:
Scanner input;
File file = new File("Latex3.bib");
input = new Scanner(file);
input.useDelimiter("#ARTICLE");
while(input.hasNext()) {
System.out.println("It should not print this");
s = input.next();
System.out.println("Token"+c+": "+s);
c++;
author = s.substring
(s.indexOf("author={")+8,
s.indexOf("}", s.indexOf("author={")+8));
System.out.println("Authors for this ARTICLE: "+author);
System.out.println();
}
(in the code, c is an int that I declared earlier in the original code for just counting and increasing by each loop).
I was getting weird exception from the code that I think it should not throw. After testing each input files, it turns out my while loop runs even when there's no "#ARTICLE" in the Latex3.bib file.
As far as I know, if the file does not have "#ARTICLE", that means that input does not get any token. So from the beginning, its hasNext() should return false. But it just wasn't.
The part what I think funny and strange is: if Latex3.bib is completely empty(not even one space bar or enter), the while loop or whatever reads it as an empty file, therefore no token is given to input.
However, if Latex3.bib has like 1~3 linebreak(enters), then that while loop runs. I have no idea why this is happening. I am suspecting that Scanner.useDelimiter(pattern) does not affect or at least cooperate perfectly with other Scanner methods like hasNext(), although I believe this won't be the real case because it's a very crucial functionality.
edit:
Just to be clear what exception I got, the StringIndexOutOfBoundsException was thrown at the point of assigning author(String). Well it's obvious because the file was empty... and there was nothing to substring or find index..

How do I make my code restart?

So I have this code and it is inside a while(MainMenu) and what i want to learn is how to restart my code.. Sometimes using MainMenu=false; and then MainMenu=true; works but sometimes it doesnt causing me to use return to crash the program because the user can cheat...
if(CitizenLoveNum<=20){
JOptionPane.showMessageDialog(null, "Your citizen are protesting against you! You need to stop them now before something really bad happens", "Citizens on Strike!", JOptionPane.WARNING_MESSAGE);
String Strike=JOptionPane.showInputDialog("Choose 1 of the options below! \n 1.Pay Them (-1000$) \n 2.Send Army (With this move you can lose from 0 to 5 men!)", "Citizens on Strike!");
if(Strike.equals("1")){
if(MoneyCount<=0){
JOptionPane.showMessageDialog(null, "Out Of Money! Try again later..", "No Money Left", JOptionPane.WARNING_MESSAGE);
return;
}
JOptionPane.showMessageDialog(null,"The Citizen's are calm once more. But try buying them recources so that won't happen again!", "Citizens on Strike!",JOptionPane.INFORMATION_MESSAGE);
MoneyCount-=1000;
MainMenu=false;
MainMenu=true;
}else{
Random Army= new Random();
int ArmyNum = Army.nextInt(5 - 0) + 0;
JOptionPane.showMessageDialog(null,"You have sent the army. You lost "+ArmyNum+" Soldiers.", "Citizens on Strike!",JOptionPane.INFORMATION_MESSAGE);
SoldierCount1-=ArmyNum;
MainMenu=false;
MainMenu=true;
}
}
It works where i putted the MainMenu=false; MainMenu=true; but it doesnt where i used return; i had to put return; because MainMenu=false; MainMenu=true; was not working. Thank you for your time :)
Edit: it usually does not work when I use an if inside an if... hope that helps
From what I can interpret, you probably have some code like this:
while(true) {
// A: Do stuff here
while(MainMenu) {
// B: Stuff before your posted code
if(CitizenLoveNum<=20){
// C: Your posted code
}
}
// D: Do other stuff here
}
If you want to go to B, use continue;.
If you want to go to D, use break;.
If you need to go to A without touching D, then enclose D in an if statement.
If your program is ending when you use continue, you probably are missing the while(true) at the top. This part is necessary to keep your code running unconditionally, forever. Otherwise, upon exiting while (MainMenu), your program will run to completion.
Also, I would like to add that
MainMenu = false;
MainMenu = true;
Is equivalent to
MainMenu = true;
Your code executes sequentially. Once you are inside the while loop, MainMenu is not checked again until you encounter a continue or all the code inside the loop finishes executing.
If you want to return to the top of the while body, you can use the keyword
continue;
Changing the MainMenu value in the while body will not cause any action becuase it gets checked before every new loop, the continue keyword will be applied immediately.
This:
MainMenu=false;
MainMenu=true;
Is the same as this:
MainMenu=true;
You're assigning a value that will be checked when it gets back to the start of the loop. If the value is true at that point, it will continue. So if you want to continue looping, set it to true. If you want to stop, just set it to false. Do one or the other, not both. Note that MainMenu=false will continue executing the current loop, and (unless it later gets set to true) will stop it from looping again.
What the return statement does is exits the function that your while loop is enclosed in entirely. If you had cleanup code after the end of your loop, you wouldn't want to return, as that would skip it.
If you want to execute more code after the loop (not exit the function yet), but you want to immediately exit the loop without finishing the current iteration, you can use the break statement.
If you want to return to the start of your loop immediately (from anywhere in the loop) and continue executing it, use the continue statement. Whether the loop executes again will depend on the value of MainMenu.

Why doesn't Scanner seem to look for a new int every time nextInt is called

Why does it continue in the while loop repeating "Please enter a valid number" and it keeps repeating without stopping to let the user input something.
while (true) { //This will continually run until something is returned, AkA the number 1 or 2
Scanner s = new Scanner(System.in);
try {
input = s.nextInt();
} catch (Exception e) {
System.out.println("Please enter a valid number!");
}
s.close();
}
When I use the debugger nothing seems to be the cause of this problem and no errors are thrown. What is causing this problem and how can it be fixed?
Closing a scanner also closes the underlying stream if it implements the Closable interface.
That's System.in in this case and, once closed, you won't be able to create a scanner using it again.
See http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#close() for the gritty detail.
But, even if you fix that problem, an exception from nextInt will not advance the stream pointer so, the next time you call it, it will find exactly the same data in the input stream again.
You need to clear out the erroneous data before trying again. Since you're accepting user input, one solution is to call nextLine and throw that away;
string junk = s.nextLine();
Remove
s.close();
And Define your Scanner object outside the while loop as it not an Issue. Btw, I've tried your code, it worked fine until I entered a input which is not an int. When I entered a string, the exception sysout statement keeps printing infinitely
So you've to add
string junk = s.nextLine();
in your Exception block like paxdiablo said so that it wont repeat continuously.

Source of Infinite Loop

Everytime I run this code, the console goes into an infinite loop printing "Please input a number". I do not understand why this is happening. Thanks in advance.
boolean check = true;
int temp = 0;
while(check==true){
try{
temp= asker.nextInt();
check = false;
}
catch(InputMismatchException e){
System.out.println("Please input a number.");
}
}
Edit: asker is a Scanner. The purpose of the code is to loop until an integer is inputted by the user.
The method asker.NextInt() is throwing an InputMismatchException, indicating that the input received from asker (assuming it's a Scanner) isn't actually an integer. This exception causes the loop to restart without setting check to false.
Print the exception within the catch block to get more information about the failure. But most likely, you're feeding your application something (lots and lots of something, if it's looping like that) that doesn't actually contain integer values.
You never want to actually "Use" try/catch--by that I mean don't use it as part of your program logic--this is what you are doing.
One big problem is that, like your app, you don't see the stack trace. Eating a stack trace in an exception is almost always wrong.
If you do have to catch an exception, handle it near the catch as well as you can, but it's better to set up your code so that the exception can't be thrown anyway.
Discard this advice if your teacher told you to do it this way, but remember in the back of your mind that it's poor form.
Also don't tell your teacher that it's poor form :) he either doesn't know in which case he won't understand why or he does know and is using this to show you how try/catch works.
What is asker, a Scanner? If nextInt() fails, it doesn't consume any input, so when you catch your exception and loop back around to try again, it ends up just reading the same bad input again.
You should do something in the catch block to consume the invalid input, so that the next time around, it can read some different input. Call asker.nextLine() maybe, and ignore the return value.
You need to break the loop and report why the loop occurs
boolean NotValid = true;
int temp = 0;
while(NotValid){
try{
temp= asker.nextInt();
NotValid = false;
break; // stop
}
catch(InputMismatchException e){
System.out.println("Please input a number. reason why:");
System.out.println(e);
NotValid = true;
}
}

Categories

Resources