This question already has answers here:
Unexpected result in simple java program
(4 answers)
Closed 7 years ago.
I'm working through a piece of code from the "Java: A Beginner's Guide 6th Ed." The section I'm on is explaining the different ways to implement a For Loop.
// Loop until an S is typed.
System.out.println("Press S to stop.");
for(int i = 0; (char) System.in.read() != 'S'; i++)
System.out.println("Pass #" + i);
My question is this - why does it execute 3 times before asking for a new keyboard input? When I type any character, it outputs:
Press S to stop.
T
Pass #0
Pass #1
Pass #2
P
Pass #3
Pass #4
Pass #5
I would expect it to run once and then wait for the next input.
I'm sure you didn't just press 'T' and 'P'.
You pressed 'T' return 'P' return.
And a return on the Windows platform is represented by two characters: carriage return (\r) followed by a line feed (\n).
So by typing 'T' return you typed 3 characters:
'T'
\r
\n
That's why.
It's not a Java thing. Exactly the same thing would have happened if you had written your program in C or Perl.
If you are just using System.in, then it's not possible to get individual keypresses on most consoles - instead you won't get any data until the return has been pressed. You can write a GUI application instead of you want to have more detailed insight into key presses.
As has been mentioned, the extra iterations represent the EOL or End of Line characters. When you type a character as input, you need to type an additional character to deliver the standard input for your program: probably the Enter key.
To see this more clearly, here's a simple amendment to your code:
// Loop until an S is typed.
System.out.println("Press S to stop.");
char read;
for (int i = 0; (read = (char) System.in.read()) != 'S'; i++)
{
System.out.print("Pass #" + i);
// specific checks for LF \n, or CR \r
if (read == '\n')
System.out.println("; char read was: \\n");
else if (read == '\r')
System.out.println("; char read was: \\r");
else System.out.println("; char read was: " + read);
}
Output:
Press S to stop.
T
Pass #0; char read was: T
Pass #1; char read was: \r
Pass #2; char read was: \n
P
Pass #3; char read was: P
Pass #4; char read was: \r
Pass #5; char read was: \n
S
From my output, and from yours, we can make a good guess that the system that we're running this loop on is a Wndows-based system. Note that different operating systems have represented this character differently: so you should expect to see different output when you run your code on a UN*X based system, like on a mac. See the wikipedia article for Newline for more information.
The java language makes a useful abstraction for this that you can use in more complex code: the standard library captures the operating-system dependent value for EOL in System.lineSeparator, see the docs for lineSeparator here
If you wanted to make it work as you were expecting, you could use a scanner like so:
Scanner scanner = new Scanner(System.in);
System.out.println("Press S to stop.");
for(int i = 0; scanner.next().charAt(0) != 'S'; i++)
System.out.println("Pass #" + i);
That gives the following output:
Press S to stop.
W
Pass #0
R
Pass #1
T
Pass #2
S
Related
I am learning some basic IO right now and I decided to play around with some code, I wrote the following:
do{
System.out.print("Enter a char: ");
char x = (char)System.in.read();
if(x == 'q'){
break;
}
}while(true);
When I run this code and provide an incorrect input (ie anything that's not 'q') I get multiple prompts instead of 1.
Enter a char: s
Enter a char: Enter a char: Enter a char:
The same does not occur when using the Scanner object to read data.
Why is this occurring?
Thanks for any help.
char x = (char)System.in.read(); This line reads your inputs char by char. When you enter one char s and then press enter, actually you have two chars as follows;
s
\n (new line char, because you press enter)
Let's say you enter ab and then press enter, it means that you have 3 chars;
a
b
\n
So your code reads your input char by char on each iteration, and on each iteration you print Enter a char:.
But as you said that Scanner works just fine with same input. Because when you read input with Scanner.nextLine() you read your whole input as one string.
If you mark your code (inside loop) with breakpoint and debug it, you will see its behavior.
In the following code from "Java: A Beginner's guide", the for loop seems to iterate more than once when a single character is typed, even though the loop control variable, i, should only be incremented by one each iteration.
The condition to enter the for loop is based on user input. The program will enter the loop and increment i by one until the character S is typed by the user. Every time the program enters the loop, i is printed out.
class ForTest {
public static void main(String args[])
throws java.io.IOException {
int i;
System.out.println("Press S to stop.");
for(i = 0; (char) System.in.read() != 'S'; i++)
System.out.println("Pass #" + i);
}
}
So it is expected that when a character other than S is typed, the program prints out Pass #0 and then waits for the user to input the next character. Oddly, it loops thrice, printing Pass #0 Pass #1 and Pass #2 before asking for user to input the next character.
Expected:
a
Pass #0
b
Pass #1
S
Actual:
a
Pass #0
Pass #1
Pass #2
b
Pass #3
Pass #4
Pass #5
S
If you change a little the program in order to debug it:
char myChar;
for(i = 0; (myChar = (char) System.in.read()) != 'S'; i++)
System.out.println("Pass #" + i + " the character from the console is: " + (byte)myChar);
}
and then run it, you will see what characters are actually comming from the input stream:
Press S to stop.
Pass #0 the character from the console is: 97
Pass #1 the character from the console is: 10
Pass #2 the character from the console is: 98
Pass #3 the character from the console is: 10
97 - is a
10 - is line feed
98 - is b
10 - is line feed
I hope it is clear now to you - if you press a + Enter, then the console returns a + line feed characters to the program, that is two characters, not one.
in for loop it cheaks the condition first and if it is true then it executes the further code i recommend you to use do while loop because it executes the code first and then it cheaks the code
Using eclipse btw.
Why does this:
public class charCounter {
public static void main(String[] args)
throws java.io.IOException {
char entry;
int count;
for (count = 0;;){
System.out.println("Press \" . \" to exit.");
entry = (char) System.in.read();
if (entry != '.')
count++;
else break;
}
System.out.println("Number of entries: " + count);
}
}
result in 3x the amount of "count" as it should be? That is when I enter a, b, and c, for example, and then press '.', it says "Number of entries: 12"
I'm reading through "Java, A Beginner's Guide" and I don't understand what I did wrong? I'm new but not stupid so I don't see the logic behind this. Is it simply a bug or too fast of a mechanic behind the for loop to be used for such short code?
It's a bug in your code. When you push <enter> you are actually inserting special (invisible) characters \r\n (also known as carriage return and newline). This is why every time you push enter you get extra characters.
You effectively have the input (spaces added for clarity only):
a \r \n b \r \n c \r \n . \r \n
even though your console looks like:
a
b
c
.
System.in.read reads one character at a time, so your loop will actually execute thrice for the sequence a\r\n, once for a and once for \r and once for \n.
I have started to learn Java, wrote couple of very easy things, but there is a thing that I don't understand:
public static void main(String[] args) throws java.io.IOException
{
char ch;
do
{
System.out.println("Quess the letter");
ch = (char) System.in.read();
}
while (ch != 'q');
}
Why does the System.out.println prints "Quess the letter" three times after giving a wrong answer. Before giving any answer string is printed only once.
Thanks in advance
Because when you print char and press Enter you produce 3 symbols (on Windows): character, carriage return and line feed:
q\r\n
You can find more details here: http://en.wikipedia.org/wiki/Newline
For your task you may want to use higher level API, e.g. Scanner:
Scanner scanner = new Scanner(System.in);
do {
System.out.println("Guess the letter");
ch = scanner.nextLine().charAt(0);
} while (ch != 'q');
Using System.in directly is probably the wrong thing to do. You'll see that if your character is changed from q to something in Russian, Arabic or Chinese. Reading just one byte is never going to match it. You are just lucky that the bytes read from console in UTF-8 match the character codes for the plain English characters.
The way you are doing it, you are looking at the input as a stream of bytes. And then, as #Sergey Grinev said, you get three characters - the actual character you entered, and the carriage return and line feed that were produce by pressing Enter.
If you want to treat your input as characters, rather than bytes, you should create a BufferedReader or a Scanner backed by System.in. Then you can read a whole line, and it will dispose of the carriage return and linefeed characters for you.
To use a BufferedReader you do something like:
BufferedReader reader = new BufferedReader( InputStreamReader( System.in ) );
And then you can use:
String userInput = reader.readLine();
To use a Scanner, you do something like:
Scanner scanner = new Scanner( System.in );
And then you can use:
String userInput = scanner.nextLine();
In both cases, the result is a String, not a char, so you should be careful - don't compare it using == but using equals(). Or make sure its length is greater than 1 and take its first character using charAt(0).
As has been mentioned, the initial read command takes in 3 characters and holds them in the buffer.
The next time a read command comes around, it first checks the buffer before waiting for a keyboard input. Try entering more than one letter before hitting enter- your method should get called however many characters you entered + 2.
For an even simpler fix:
//add char 'ignore' variable to the char declaration
char ch ignore;
//add this do while loop after the "ch = (char) System.in.read();" line
do{
ignore = (char) System.in.read();
} while (ignore != '\n');
this way 'ignore' will cycle through the buffer until it hits the newline character in the buffer (the last one entered via pressing enter in Windows) leaving you with an fresh buffer when the method is called again.
Hey Im taking coding lessons at school but the teacher does not explain that well so we have to look for info online which I did, but I was not able to find the error in my code, can you help me please?
char end='s';
do{
System.out.println("Tipo de boleto");
char boleto = (char) System.in.read();
switch (boleto){
case 'a':
System.out.println("El boleto cuesta $120.00");
System.out.println("Otro boleto (s/n)?");
end = (char) Integer.parseInt(entrada.readLine());
continue;
case 'n':
System.out.println("El boleto cuesta $75.00");
System.out.println("Otro boleto (s/n)?");
end = (char) Integer.parseInt(entrada.readLine());
continue;
case 'i':
System.out.println("El boleto cuesta $60.00");
System.out.println("Otro boleto (s/n)?");
end = (char) Integer.parseInt(entrada.readLine());;
continue;
default:
System.out.println("Error" );
break;
}
}
while (end == 'n');
Exception
run: Tipo de boleto a El boleto cuesta $120.00 Otro boleto (s/n)?
Exception in thread "main" java.lang.NumberFormatException: For input string: "" at
java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:592) at
java.lang.Integer.parseInt(Integer.java:615) at
asjidbhahsjksbd.Asjidbhahsjksbd.main(Asjidbhahsjksbd.java:16) Java Result: 1
BUILD SUCCESSFUL (total time: 7 seconds)
See, you are trying to parse "" as an Integer whichwill throw NumberFormatException. You have to check for null and isEmpty() in this order and then try to parse the string as an integer.
You are getting exception in this line , i think you are getting "" blank String from readLine() method
end = (char) Integer.parseInt(entrada.readLine());
So Do like this
String input=entrada.readLine();
if(input!=null && !input.equals(""))
{
end = (char) Integer.parseInt(input);
}
I suggest you to use google guava libraries which is having a utility function
Strings.isNullOrEmpty(inputString)//Checks String for both null and empty
Update
As #ajb suggested :
If you want to convert s and n into character than don't use your code snippet
instead of Parsing an Integer
Use
char c=input.charAt(0);
you should replace continue statement with a break. putting continue will skip the current iteration and the while condition will not be evaluated.
This does not do what you think it will:
end = (char) Integer.parseInt(entrada.readLine());
This line reads a string. It then assumes the string is a number, and determines the number. If the user actually enters "s" or "n", it throws an exception, because "s" and "n" are not numbers. The number is then treated as the ASCII value of a character. The result is that the loop will test whether the user types in the string "110", since 110 is the ASCII value of the character n.
There are several ways to fix this; here's one:
end = entrada.readLine().charAt(0);
This returns the first character of whatever line the user types in. This is a sloppy solution because it doesn't work if the user hits ENTER on an empty line (it will throw an exception). Better:
String answer = entrada.readLine();
if (answer.isEmpty()) {
end = 'n'; // treat an empty string like 'n'
} else {
end = answer.charAt(0);
}
Also, I think the while might be wrong. while (end == 'n') means the program will loop back if the user enters n, which I think is the opposite of what you want.
P.S. There are other errors that I didn't catch, that others have pointed out; using continue is wrong--use break to leave the switch statement. And reading one character with System.in.read() is a problem, because the user will type in a character, but the character won't get into the program until the user types ENTER, and then readLine() will get the rest of this first line, instead of asking for another line. But I usually don't use System.in.read() so I'm not completely sure what this does without trying it.