Tokens, Lines, and the hasNext Cascade
Try This First
A program reads an age with sc.nextInt(), then reads a full name on the next line with sc.nextLine(). The age comes back fine. The name comes back as an empty string. Same Scanner, two read calls in a row, and one of them silently returns nothing. What broke?
Reveal
nextInt() reads the digits of the number and stops, leaving the Enter key (the newline character) at the end of that line still unread. The very next nextLine() reads from the cursor up to that newline, finds nothing in between, and hands you an empty string. The number and the Enter after it are two separate things in the file, and nextInt() only takes the first one. The fix is an extra sc.nextLine() right after nextInt() to swallow that leftover newline before you read the real line.
Where you are: Week 0 review > Tokens, lines, and the hasNext cascade
Before You Start
Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.
See Reading a File with Scanner if any of the above felt unfamiliar.
Not sure? Take the 60-second self-check.
Try each from memory, then read the answer under it.
- What does a
Scannerover a file let you do? Read the file piece by piece, the same way a keyboardScannerreads input. - What guards a read loop so it stops at the end of the file? A
hasNextstyle check, asked before each read.
The One Idea
Every read from a Scanner has a matching guard, and the bugs in this lesson all come from breaking that pairing. hasNextLine goes with nextLine, hasNextInt goes with nextInt, hasNext goes with next. Read it in the wrong mode and you either crash or, like the puzzle above, get a silent empty string. The rest of the page works out why, so that by the end you can look at a file and pick the right pair without guessing.
How It Works
The file’s shape decides the mode:
| File format | Mode | Guard | Read |
|---|---|---|---|
| One record per line | Line | hasNextLine() |
nextLine() |
| Whitespace-separated words | Token | hasNext() |
next() |
| Integers, possibly several per line | Token, typed | hasNextInt() |
nextInt() |
Line mode:
while (sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println(line);
}
Token mode for integers:
int sum = 0;
while (sc.hasNextInt()) {
sum = sum + sc.nextInt();
}
First time seeing "sentinel"? Open for a 20-second refresher.
A sentinel is a special value placed in data to signal “stop here.” Instead of counting items ahead of time, the loop watches for the sentinel and exits when it appears. A word like STOP at the end of an integer file is a classic example.
hasNextInt() returns false at the end of the file and at the first token that is not a valid integer. A file that ends with the word STOP makes hasNextInt() return false at STOP, so the loop stops on its own with no if inside it.
The nextInt then nextLine trap
The usual wrong picture is this: students assume nextInt() consumes the whole line the number sits on, Enter key and all, so the cursor jumps to the start of the next line. It is an easy belief to land on, because when you type at the keyboard the number and the Enter feel like one action. In the file they are two characters. nextInt() takes the digits and stops; the newline is still sitting there unread.
Picture the buffer as the actual characters, with ^ marking where the cursor rests after nextInt() reads 30:
3 0 \n A l i c e \n
^
The cursor is parked right before the \n. An immediate nextLine() reads from ^ to that \n, finds nothing in between, and returns an empty string. The name Alice is still waiting on the line below. Two standard fixes:
- Call an extra
sc.nextLine()right afternextInt()to consume the leftover newline, then read the real line. - Read every line with
nextLine()and convert withInteger.parseInt(line.trim()), so the modes never mix.
Worked Example: Predict, Then Check
A file holds the integers 10 20 30 on one line. The token loop above sums them. Predict the result.
Reveal
60. Each pass hasNextInt() peeks true and nextInt() reads the next integer: 10, then 20, then
- The fourth peek reaches the end of the file, returns false, and the loop exits with the sum 60.
A Common Mistake
The most common file-reading bug is a guard that does not match its read, such as hasNextInt() guarding a nextLine(), or reading with nextLine() right after nextInt() without consuming the newline. Keep the guard and the read in the same family, and remember that nextInt() does not move past the end of its line. (Source: BJP (Reges and Stepp), Ch 6.)
Go Deeper (optional)
For the curious: a Scanner splits input into tokens on whitespace and also tracks line boundaries, so the two modes share one cursor. Mixing them is what causes the trap. Many programs avoid the issue entirely by reading whole lines and parsing each line themselves, which keeps one consistent mode and makes the cursor easy to reason about.
Check Yourself
Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.
Check your understanding
Which guard matches a nextLine() read?
When does hasNextInt() return false?
After sc.nextInt() reads a number, an immediate sc.nextLine() returns what?
A file has one full name per line. Which mode fits?