Scanner and console applications
Reading numbers and lines from the keyboard, building a small interactive program, and common Scanner traps.
Finished reading?
Mark this session so you can track where you are.
Reading numbers and lines from the keyboard, building a small interactive program, and common Scanner traps.
Finished reading?
Mark this session so you can track where you are.
Every program you have written so far has done all the talking. It prints, it computes, it prints again, and it never once stops to ask you anything. Real tools are a conversation: they ask, you answer, they react. Today we give your program ears. We read what a person types at the keyboard.
Scanner that reads from the keyboard, and explain what System.in is.nextInt or nextDouble, one word with next, and a whole line with nextLine.nextInt-then-nextLine trap that eats your input.Scanner lives in java.util and you must import it. You also need loops for the interactive loop at the end, and if-else to react to what was typed. Nothing heavier.Until now, output went out through System.out. There is a matching door for input called System.in. Think of System.out as the program's mouth and System.in as its ear. The trouble is that System.in on its own is raw and awkward to use: it hands you single bytes, not tidy numbers and words. We want something that reads a whole int or a whole line for us. That convenient wrapper is the Scanner.
A Scanner is an object. It does not live in the core of the language the way Stringdoes. It lives in a package called java.util, so before you can use it you must import it, exactly the way you learned last session.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("What is your name? "); String name = sc.nextLine(); System.out.println("Hello, " + name); }}Sample run (the line after the prompt is what the user typed): What is your name? Aisha Hello, Aisha
Read that top to bottom. The import line brings the name Scanner into scope so you can write it plainly. Then new Scanner(System.in) builds a scanner and points it at the keyboard. After that, sc.nextLine() waits. The program genuinely pauses there, doing nothing, until the person types something and presses Enter. Whatever they typed comes back as a String, which we store in name and then print.
System.out.print, with no ln. That keeps the cursor on the same line, so the person types their answer right after the question mark instead of on a fresh line below it. It is a small thing that makes a console app feel polished.A scanner does not have one read method, it has a small family of them, and each one reads a different shape of input. Picking the right one is most of the skill. Here are the four you will use constantly.
nextInt() reads the next whole number and gives you an int.nextDouble() reads the next number that may have a decimal point and gives you a double.next() reads a single token: one run of non-space characters, one word.nextLine() reads everything up to the end of the current line, spaces and all.The word token is worth pinning down. A scanner splits what you type at the whitespace, the spaces and newlines, into pieces. Each piece is a token. If you type red green blue, that is three tokens. next() grabs exactly one of them and stops at the space. nextLine() ignores the spaces and grabs the whole line as one string.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Type three words: "); String word = sc.next(); System.out.println("First word only: " + word); }}Sample run: Type three words: red green blue First word only: red
Look closely at the sample run. The person typed three words, but next() only kept the first, red. It read one token and stopped at the first space. The other two words are still sitting unread in the buffer, waiting for the next read call that never comes. That leftover is harmless here, but it is the seed of the bug we will meet shortly. Hold that thought.
nextInt()42, as an int.nextDouble()3.5, as a double.next()nextLine()String.input(), which always hands you the whole line as a string and lets you convert it yourself. Java's scanner instead has a method per shape. The upside is that nextInt() gives you a real int with no conversion step. The catch is that you have to know which method matches what the person will type.Let us build something that asks two questions and reacts. We will ask for a name and an age, then greet the person and say something about their age. This is the shape of almost every console tool: prompt, read, prompt, read, then compute and respond.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("What is your name? "); String name = sc.next(); System.out.print("How old are you? "); int age = sc.nextInt(); int nextYear = age + 1; System.out.println("Hello, " + name + ". Next year you will be " + nextYear + "."); }}Sample run: What is your name? Aisha How old are you? 20 Hello, Aisha. Next year you will be 21.
Trace the conversation. The program prints the first prompt and waits. The person types Aisha and presses Enter, so next() returns "Aisha". The program prints the second prompt and waits again. The person types 20, so nextInt() returns the int 20. Now both values are in hand, so we add one and print the greeting. Because age is a real int, the + 1 is ordinary arithmetic, not string joining.
Here is the single most common bug a beginner hits with Scanner, and almost everyone hits it once. Suppose you read a number, then want to read a full line of text afterward. The obvious code looks right and is wrong.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Enter your age: "); int age = sc.nextInt(); System.out.print("Enter your full name: "); String fullName = sc.nextLine(); System.out.println("Hello, '" + fullName + "' (age " + age + ")"); }}Sample run (notice the bug): Enter your age: 20 Enter your full name: Hello, '' (age 20)
Read the sample run again, slowly. The program never gave the person a chance to type their name. It printed the second prompt and then immediately printed the greeting, with fullName coming out empty, shown here as the two quote marks with nothing between them. The keyboard was never even touched for the name. So where did the name go, and why is it blank?
20 and press Enter, two things land in the buffer: the characters 20 and an invisible newline from the Enter key. nextInt() reads the 20 and stops. It leaves that newline sitting there, unread. The next nextLine() reads from the current spot to the end of the line, finds only that leftover newline, and so returns an empty string at once, without ever pausing for the person. It is not your input that is wrong. It is the crumb the number read left behind.The cure is one extra line. After the nextInt(), call sc.nextLine() once on its own and throw the result away. That spare call swallows the stranded newline. Now the head is sitting at the start of a fresh line, and your real nextLine() behaves the way you expected.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Enter your age: "); int age = sc.nextInt(); sc.nextLine(); // consume the leftover newline after the number System.out.print("Enter your full name: "); String fullName = sc.nextLine(); System.out.println("Hello, '" + fullName + "' (age " + age + ")"); }}Sample run (now it works): Enter your age: 20 Enter your full name: Aisha Khan Hello, 'Aisha Khan' (age 20)
That is the whole fix: one throwaway sc.nextLine() sitting between the number read and the line read. Because we wanted a full name with a space in it, nextLine() was the right tool, and once the leftover newline is gone it captures Aisha Khan in full, space included.
Reading a number with
nextIntornextDoubleleaves the Enter newline in the buffer. If anextLinecomes next, add a throwawaysc.nextLine()between them to clear it.
nextLine() and convert numbers yourself, or always clear the buffer with a spare sc.nextLine() right after every nextInt or nextDouble. Pick one habit and keep it, and this bug stops visiting you.One read is a single question. A real tool keeps asking until you are done. The usual pattern is to read over and over inside a loop, and stop when the person types a special agreed value that means “I am finished”. That special value is called a sentinel: a guard standing at the exit.
Below, we keep reading numbers and adding them to a running total. The sentinel is 0. As soon as the person types 0, the loop ends and we report the sum. Every other number gets folded into the total.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int total = 0; System.out.print("Enter a number (0 to finish): "); int n = sc.nextInt(); while (n != 0) { total = total + n; System.out.print("Enter a number (0 to finish): "); n = sc.nextInt(); } System.out.println("Total: " + total); }}Sample run: Enter a number (0 to finish): 5 Enter a number (0 to finish): 12 Enter a number (0 to finish): 3 Enter a number (0 to finish): 0 Total: 20
Walk the sample run against the code. We read one number before the loop, called the priming read, so the while condition has something to test. The person types 5, which is not 0, so we enter the loop, add it to total, prompt, and read again. The pattern repeats for 12 and 3. Then they type 0: the condition n != 0 is now false, the loop stops, and we print 5 + 12 + 3, which is 20. The sentinel itself is never added, because we read it, then test, then leave before adding.
0 here because adding zero is pointless anyway, so it is safe to reserve as the stop signal. If 0 were a number you genuinely wanted to total, you would need a different sentinel, perhaps a negative number, or you would read lines and check for a word like done. The rule is simple: the sentinel must be a value the real data will never legitimately take.Here is the shape of a complete little console app, combining a loop, a sentinel, and a branch on what was typed. It reads a command word each time and acts on it, stopping when the person types quit. Because every entry is a single word read with next(), there is no newline trap to worry about.
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Command (hello / bye / quit): "); String cmd = sc.next(); while (!cmd.equals("quit")) { if (cmd.equals("hello")) { System.out.println("The program says hi."); } else if (cmd.equals("bye")) { System.out.println("The program waves goodbye."); } else { System.out.println("Unknown command: " + cmd); } System.out.print("Command (hello / bye / quit): "); cmd = sc.next(); } System.out.println("Shutting down."); }}Sample run: Command (hello / bye / quit): hello The program says hi. Command (hello / bye / quit): bye The program waves goodbye. Command (hello / bye / quit): quit Shutting down.
This is the same priming-read pattern as the adder, with a string sentinel and an if-else chain in the middle. We compare strings with cmd.equals("quit"), never with ==, because for objects == asks “the very same object?” while equalsasks “the same text?”, and here we mean the text. That distinction was the heart of references and equality.
Try each one yourself first, then open the answer.
Scanner sc = new Scanner(System.in); set up, and what does System.in stand for?hello world and presses Enter. The program calls sc.next(). What string comes back, and what is left unread?nextLine() placed right after a nextInt() often comes back empty.4, then 4, then 0. What does the program print as the total, and why is one of those 4 values not the sentinel even though the sentinel is also a single digit?Take these away. They continue exactly what we just did.
next(), then their favourite quote with nextLine(), then prints them together. Make it work correctly even though a line read follows a word read. Show the sample run you expect.double, ignoring the sentinel. Be careful not to count or include the 0.add reads one number and adds it to a running total, show prints the current total, and quit stops. Sketch the sample run.next() and nextLine(), and give one situation where choosing the wrong one would change what your program reads.