user974906
user974906

Reputation: 19

Simple Java Scanner issue

I haven't been doing Java long. In this script where it says Thread.sleep(10000); you can still type (I use eclipse) I really don't know why it enables you to type in the output when I have no uno.nextLine(); before or after the 10 second waiting. Please help! Thanks

import java.util.Scanner;
class Tutoiral{
        public static void main (String args[])throws InterruptedException {
            Scanner uno = new Scanner(System.in);
            Scanner uno1 = new Scanner(System.in);
            System.out.println("What's your name?");
            String name = uno.nextLine();
            System.out.println("Hi, " + name + ". Feel free to type whatever you want :) BUT DON'T SPAM!!!!!");

            String typed = (": ") + uno1.nextLine();
            System.out.println(name + typed + " (5 types remaining)");
            uno.nextLine();
            System.out.println(name + typed + " (4 types remaining)");
            uno.nextLine();
            System.out.println(name + typed + " (3 types remaining)");
            uno.nextLine();
            System.out.println(name + typed + " (2 types remaining)");
            uno.nextLine();
            System.out.println(name + typed + " (1 type remaining)");
            uno.nextLine();
            System.out.println(name + typed + " (If you type one more time, then I'll... I'll block you! Ha!)");
            uno.nextLine();
            System.out.println(name + typed + " (tut tut tut. You have been blocked for a while for spam. Don't type EVER again ok?)");
            Thread.sleep(10000);
            System.out.println("Ok, you can type again. But I wouldn't If I were you.");

Upvotes: 2

Views: 860

Answers (3)

Mechanical snail
Mechanical snail

Reputation: 30637

Console input (and hence Scanner) does not work the way one might think.

A console program has an input stream called standard input, or stdin. You can think of this as a queue of characters for the program to read.

When you type a character into a console program, it normally has 2 effects:

  1. The terminal immediately prints the character to the screen.
  2. The character is appended to the program's standard input. (Technically this only happens each time you type a newline (Enter).)

These actions are performed by the OS, so they work regardless of whether your program is currently reading data from stdin. If the program is waiting for data, it will immediately process it (ignoring input stream buffering). But if not (e.g. it is processing something else, or in your case sleeping for 10 seconds), the character just sits in the queue until the program reads it.

Scanner does not actually prompt the user for a line of text. How it works: When a program tries to read from stdin, if there is no data available the program blocks until the OS feeds it enough data. Scanner.nextLine() reads characters from stdin until a newline has been read, at which point it returns the characters entered since the last newline. This means that typically, Scanner.nextLine() will cause the program to wait until the user presses Enter.

However, note that if you input more than one line of text at once, e.g. by pasting several lines into the terminal, or by shell pipes or redirection (such as with java MyProgram < file.txt), the program will keep running until it has eaten all of the input.

While your program is sleeping, the user can still type and append to your program's stdin! (Try adding a few copies of System.out.println(name + typed + uno.nextLine()); at the end of your program, and see what happens when you type while your program is sleeping.)

Therefore, to prevent the user from typing stuff that your main program will read, you need to do 2 things while your main thread sleeps:

  1. Tell the console to stop echoing characters typed to the console.
  2. Read any characters fed to stdin, and discard them.

You can do both of these by calling System.console().readPassword() in another thread (and discarding its output). That way, after 10 seconds your main thread will wake up and kill the password-reading thread. But by then, anything the user typed while the main thread was sleeping will already have been read (removed from stdin) and discarded by the other thread, so in effect the user has been unable to type on the console. (See aioobe's answer for code of this.)

Upvotes: 0

aioobe
aioobe

Reputation: 420991

I really don't know why it enables you to type in the output when I have no uno.nextLine();

The call to nextLine will read (and return) characters from standard input. The console will print the characters entered by the user, regardless if you read them or not.

Unfortunately (for you) it's not possible to prevent the user from entering data on standard input.


One workaround would perhaps be to disable "local echo", i.e. avoid printing the characters entered by the user. I'm not sure how to do this. A nasty approach would be to do System.console().readPassword().

Playing around with it, I came up with the following super-nasty hack workaround (Beware that Thread.stop() for instance, is deprecated for good reasons.):

public static void turnOffLocalEcho(long ms) {
    Thread t = new Thread() {
        public void run() {
            while (true) { System.console().readPassword(); }
        }
    };

    t.start();

    try {
        Thread.sleep(ms);
    } catch (InterruptedException ie) {
    }

    t.stop();
}

Just replace your call to Thread.sleep(10000) with a call to this method:

turnOffLocalEcho(10000);

Upvotes: 2

aaazalea
aaazalea

Reputation: 7920

uno will process the input typed whenever (while sleeping, working, or waiting for input) when one of its nextSomething() methods is called. you need not call nextSomething() and parse the input, but the user can enter it either way.

Upvotes: 0

Related Questions