Reading input from user

while doing, reading input exercises

code:
Scanner scanner= new Scanner(System.in);
System.out.println(“Enter the age:”);
Byte age= scanner.nextByte();
System.out.println(“Age is:”+age);
System.out.println(“enter name:”);
String name= scanner.next();
System.out.println(“you’re :”+name);
System.out.println(“enter role:”);
String role= scanner.nextLine();
System.out.println(“role is:”+role);

output:
Enter the age:
24
Age is:24
enter name:
praveen
you’re :praveen
enter role:
role is:

I’m not able to enter the role here. i mean not able to give it as input. any help on this pls.

What I see is you used nextLine().

Why?

I don’t know this method yet but I think it does not do what we’d expect.
See API.

Just keep using next().

next() and nextLine() have very different behaviors so you cannot just change them out arbitrarily. In particular, the nextLine behavior is very nuanced. Here is the Javadoc describing it:

Advances this scanner past the current line and returns the input that was skipped. This method returns the rest of the current line, excluding any line separator at the end. The position is set to the beginning of the next line.

Since this method continues to search through the input looking for a line separator, it may buffer all of the input searching for the line to skip if no line separators are present.

NOTE: By “current line” it means “wherever the current pointer happens to be” which may include some already buffered content.

The next() method will return the next “token” which by default breaks on any whitespace, so you could not enter a full name with a space using the next method. Here is its Javadoc:

Returns the next token if it matches the specified pattern. This method may block while waiting for input to scan, even if a previous invocation of hasNext(Pattern) returned true. If the match is successful, the scanner advances past the input that matched the pattern.

So when the next() method is called, it also has to read the whole line up to the next newline so something like “praveen\n” is put into the buffer (where “\n” is the newline). It uses the newline as a delimiter for the token that it returns so it returns “praveen” leaving the newline in the buffer “\n”. Then when nextLine() is called, the already buffered content already contains a new line so it just returns everything preceeding that newline (which is nothing) without grabbing anything else from System.in (emptying the buffer).

It is easier to see if you wrapped the “role” printed out with single quotes:

Scanner scanner = new Scanner(System.in);
System.out.print("Enter age: ");
Byte age = scanner.nextByte();
System.out.println("Age is: " + age);
System.out.print("Enter name: ");
String name = scanner.next();
System.out.printf("You are: '%s'%n", name);
System.out.print("Enter role: ");
String role= scanner.nextLine();
System.out.printf("Role is: '%s'%n", role);

If you want to clear out that buffer, you need to call readLine() a second time so that we get past the buffered newline and then it will block until you get new input through System.in. To keep it clear what we are doing, we can clear the buffer directly after we get the name:

Scanner scanner = new Scanner(System.in);
System.out.print("Enter age: ");
Byte age = scanner.nextByte();
System.out.println("Age is: " + age);
System.out.print("Enter name: ");
String name = scanner.next();
scanner.nextLine();
System.out.printf("You are: '%s'%n", name);
System.out.print("Enter role: ");
String role= scanner.nextLine();
System.out.printf("Role is: '%s'%n", role);

This prints out the expected results.

3 Likes

Thanks for the precisions Jason.

Man I will regret my simple Console.ReadLine(); from C#. :sweat_smile:

I was just doing some exercices and took the occasion to try those things;

Actually it did not seem to be problematic for what I did.

Could you provide a practical example where it may behave in unexpected way? That would be much appreciated.

Regards.

I think what this topic shows is a prime example of where Scanner can be confusing. All of the other next methods read only one token, but when getting content from the command line they may buffer additional content. If you always use nextLine and parse the content yourself then you would not notice this, but if you mix other next methods you can see unexpected behavior because you expect that it will request new data from the command line (which is not necessarily the case). As such, Scanner does not pass the principle of least surprise (that is to say, the behavior is not the most obvious thing).

Note: Scanner is a bit less surprising when reading data from a file source.

2 Likes

Hi Jason, i kinda got confused. could u pls brief the difference of next() and nextLine() with a example.

it would help me understand better.
thanks :slight_smile:

The basic idea is that next() returns the next “token” (string of non-whitespace characters) that it finds (skipping any initial whitespace and stopping when it finds another whitespace or the end of the String, whichever comes first). nextLine() returns everything from the current position (or marker) - including whitespace - until it finds a newline character (or the end of the String, whichever comes first).

To make this easier to reason about, let us take the console out of it and imagine we are just reading from a file with the following contents:

Hello, my name is Bond
James Bond

If we were to put that content into a file called source.txt we could pass it to a Scanner like this:

try (var scanner = new Scanner(new FileInputStream("source.txt"))) {
  ...
}

NOTE: I am using a try-with-resources block since Scanner is AutoCloseable to make sure we close the resource at the end of the block.

Calling the Scanner’s next() method would return the String "Hello," and leave a marker after "Hello," (including the space). In contrast, calling the Scanner’s nextLine() method will return the String "Hello, my name is Bond" and leave a marker at the start of the second line (dropping the newline character).

Now things get interesting when we see what happens if we start mixing and matching. If we call next() once and then next() again, we will get "my" for the second string with a marker after "my" (including the space). In contrast, if we call next() and then nextLine() we get " my name is Bond" (starting with the space after "Hello,") for the second string with a marker at the start of the second line (dropping the newline character).

Now let us suppose we get through all of the tokens on the first line by calling next() five times to pick up the strings "Hello,", "my", "name", "is" and "Bond" while leaving the marker at the end of the first line before the newline character. Calling nextLine() will return an empty string ("") because the Scanner looks for the next newline character and returns everything preceeding it (which is an empty string since the very next character is a newline character). Try it yourself if you are interested to see the result:

try (var scanner = new Scanner(new FileInputStream("source.txt"))) {
  System.out.println("'"+scanner.next()+"'");
  System.out.println("'"+scanner.next()+"'");
  System.out.println("'"+scanner.next()+"'");
  System.out.println("'"+scanner.next()+"'");
  System.out.println("'"+scanner.next()+"'");
  System.out.println("'"+scanner.nextLine()+"'");
}

What gets tricky when System.in is the InputStream for our Scanner is that we must type a newline character to return control to the program and finish sending input. So input at the command line always ends with a newline character. Methods like nextByte() and next() can basically ignore the newline character because they are looking for the next token and will skip over intervening whitespace (including newline characters). The nextLine() method is just looking for the next newline character after the marker (which is placed right after the previous token that was found). With that in mind, take a look at my previous answer and see if you now understand the behavior of your program.

3 Likes

Thank u Jason, it was a crystal clear explanation.

1 Like