📁 I/O, Files & Networking · Intermediate

Console & standard streams in Java

System.in/out/err, BufferedReader vs Scanner performance.

🧩 The mysteryYou read a number with nextInt(), then a name with nextLine(). The name comes back empty — the program didn't even wait for you to type. Ghost input?

Three pipes

Every program gets three standard streams: **System.in** (bytes typed or piped in — an InputStream), **System.out (normal output) and System.err** (errors and diagnostics) — both PrintStreams. They're separate: in a shell, > redirects stdout and 2> redirects stderr.

Scanner vs BufferedReader

**Scanner** is convenient — nextInt(), nextLine() — but it tokenizes with regular expressions and is slow on big input. **BufferedReader.readLine() plus Integer.parseInt** is much faster.

var in = new BufferedReader(
    new InputStreamReader(System.in));
String line = in.readLine();
System.err.println("debug: " + line);
🔮 Predict it

The ghost line

What does this print?

var sc = new Scanner("7 8\nhello");
int a = sc.nextInt();
int b = sc.nextInt();
String rest = sc.nextLine();
System.out.println("[" + rest + "]");
  1. [hello]
  2. []
  3. [7 8]
Show the answer

[] — nextInt() stops right after the number and leaves the line break behind. nextLine() returns the rest of that same line: empty.

Fixing the ghost line

After nextInt(), call **nextLine() once to consume the rest of the line**, then read the real line. Or read whole lines with nextLine()/readLine() and parse numbers yourself.

int age = sc.nextInt();
sc.nextLine();               // eat line end
String name = sc.nextLine(); // real input
⚠️ The trap

Closing System.in

Closing a Scanner **also closes System.in — and the standard input can't be reopened**. The next Scanner(System.in) throws **NoSuchElementException. Create one** Scanner for the program and don't close it.

int askNumber() {
    try (var sc = new Scanner(System.in)) {
        return sc.nextInt();
    } // closes System.in forever!
}

Reading one million numbers

✗ Scanner
var sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
    sum += sc.nextInt();
}

Regex-based tokenizing for every number: slow.

✓ BufferedReader
var br = new BufferedReader(
    new InputStreamReader(System.in));
for (int i = 0; i < n; i++) {
    sum += Integer.parseInt(br.readLine());
}

Big chunked reads and cheap parsing.

💼 In the real world

Pipes on the job

Command-line tools write results to stdout and errors to stderr so scripts can pipe one and log the other; containers collect both as logs. Competitive programmers switch from Scanner to BufferedReader when big inputs time out.

Key takeaways

  1. System.out and System.err can be redirected separately
  2. Scanner: easy tokens, but regex-based and slow
  3. BufferedReader.readLine(): fast for large input
  4. nextInt() leaves the rest of the line unread
🤯 Did you know?

On Unix-like systems the three streams have fixed file descriptor numbers: 0 for stdin, 1 for stdout, 2 for stderr — that's where the 2> in shell redirection comes from.

Practice questions

What does this print?

var sc = new Scanner("42\nworld");
int n = sc.nextInt();
String line = sc.nextLine();
System.out.println(n + "[" + line + "]");
  1. 42[world]
  2. 42[]
  3. 42[42]
  4. Throws InputMismatchException
Check your answer

42[]. nextInt reads only "42" and leaves the line break behind. nextLine then returns the rest of that line: an empty string.

A program reading one million numbers from System.in with Scanner is too slow. What's the usual fix?

  1. Read with BufferedReader and parse with Integer.parseInt
  2. Call System.in.reset() between numbers
  3. Use System.console().readLine() for each number
  4. Read with Scanner in a parallel stream
Check your answer

Read with BufferedReader and parse with Integer.parseInt. Scanner tokenizes with regular expressions and does a lot of checking per token. BufferedReader reads big chunks and parsing manually is far cheaper.

Next: talking to the web — Java 11's HttpClient.