Why my method being called 3 times after System.in

2019-01-20 15:07发布

问题:

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

回答1:

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');


回答2:

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).



回答3:

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.