The nextLine after nextInt trap
The single most common file-I/O bug in CS1 looks like this:
int n = sc.nextInt();
String name = sc.nextLine(); // students expect: the next line
// actually returns: the empty string ""
The diagnosis: nextInt() reads the digits of n and stops immediately after the last digit. The newline character that ended the line is still in the buffer. nextLine() then reads from the current position up to the next newline, which is right there. The return is the empty string, and n's line is effectively "consumed."
This is not a bug in Scanner; it is the documented behavior. The two methods have different rules about where they leave the cursor, and those rules collide.
Reading the buffer character by character
Imagine the file as a stream and a ^ marking the cursor:
^42\nJessica\n
After sc.nextInt(), the cursor sits between the 2 and the \n:
42\nJessica\n
^
Now sc.nextLine() runs. Its rule is "consume up to and including the next newline; return what came before." The next newline is the very next character. So the call returns "" and advances the cursor:
42\nJessica\n
^
A second sc.nextLine() returns "Jessica".
The two standard fixes
Fix A: flush the newline. After every token-mode read on a line of its own, call sc.nextLine() once and throw the result away:
int n = sc.nextInt();
sc.nextLine(); // discard the newline that nextInt left behind
String name = sc.nextLine();
Fix B: stay in token mode the whole time. If the file's structure permits it, use next() (single token) or nextInt()/nextDouble() (typed token) for every read. Token-mode reads skip whitespace including newlines, so the trap never appears:
int n = sc.nextInt();
String name = sc.next(); // single token; no trap
The CSCD 210 typed-file convention is fix B for the value lines (every value is one token) and Fix A's avoid the mix variant for the type tag: the tag is read with nextLine() before any token-mode reads happen, so there is no trailing whitespace from a prior call.
In other languages
- Python: the line-vs-token distinction does not exist in the standard library;
input()and iterating over a file always return whole lines, and.split()does the tokenizing. - C:
fscanf("%d", &n)leaves the newline in the buffer the same waynextIntdoes;fgetsreads a line including the newline. The same trap exists. - C++:
cin >> n; getline(cin, name);has the identical trap with the identical fix (cin.ignore()).