Input from User

About

In Java, there are several ways to take input from the user. Here are some common methods.

Using Scanner Class

About

The Scanner class is a standard class in Java used for taking input from the user. It's part of the java.util package.

  • Supports reading input from various sources, including:

    • System.in (keyboard/console input)

    • Files (File object)

    • Strings (String object)

  • Provides methods to parse primitive data types (int, double, etc.) and strings.

  • Scanner class in Java is not thread-safe. It is not synchronized, which means that if multiple threads access a Scanner instance concurrently, and at least one thread modifies it (e.g., advances the scanner's position or modifies its delimiter), it can lead to undefined behavior.

How to Use Scanner Safely in Multithreaded Applications?

Synchronize External Access: Wrap the Scanner usage in a synchronized block or method to prevent concurrent access:

synchronized(scanner) {
    String line = scanner.nextLine();
}

Thread-Local Scanner Instances: Use a separate Scanner instance for each thread to avoid shared state:

ThreadLocal<Scanner> threadLocalScanner = ThreadLocal.withInitial(() -> new Scanner(System.in));

Example

// Reading from Console
Scanner scanner = new Scanner(System.in);

// Reading from a File
import java.io.File;
import java.io.FileNotFoundException;

File file = new File("example.txt");
Scanner scanner = new Scanner(file);

// Reading from a String
String data = "123 apple 45.67";
Scanner scanner = new Scanner(data);
// Basic Input Reading
String input = scanner.nextLine(); // Reads entire line
String word = scanner.next();     // Reads a single word/token
int number = scanner.nextInt();   // Reads an integer
double value = scanner.nextDouble(); // Reads a double
long value = scanner.nextLong(); // Reads a long
boolean flag = scanner.nextBoolean(); // Reads a boolean

// Token Delimiters. The default delimiter is whitespace. To change the delimiter
scanner.useDelimiter(","); // Now tokens are separated by commas

// Checking for Input
// hasNext(): Checks if there's another token.
// hasNextInt(), hasNextDouble(), etc.: Checks for specific data types.
if (scanner.hasNextInt()) {
    int value = scanner.nextInt();
}

// Skipping Input
// skip(String pattern): Skips a specific pattern.
// nextLine(): Advances to the next line.

// Exception Handling
// Handling Input Mismatch
try {
    int number = scanner.nextInt();
} catch (InputMismatchException e) {
    System.out.println("Please enter a valid number.");
}

// FileNotFoundException
try {
    Scanner fileScanner = new Scanner(new File("file.txt"));
} catch (FileNotFoundException e) {
    System.out.println("File not found.");
}

// Closing the Scanner
// Always close the scanner to release system resources:
scanner.close();
package src.main.java;

import java.util.Scanner;

public class Application {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter your name: ");
        String name = scanner.nextLine();
        System.out.println("Hello, " + name + "!");
        scanner.close();
    }
}

Among all the methods mentioned here, using the Scanner class is probably the most commonly used method for taking input from the user in Java, especially for beginner-level programming and simple console-based applications.

Limitations of Scanner

  • Not suitable for reading large files efficiently.

  • Limited control over complex parsing; use libraries like Apache Commons CSV or Jackson for structured data.

Using BufferedReader Class

About

The BufferedReader class, along with InputStreamReader, can be used to read input from the user. This method is useful when dealing with input streams. It's part of the java.io package. It is especially useful when working with large input data as it buffers the data to improve reading performance.

  • Reads data in chunks to minimize I/O operations.

  • Works well for reading text files or console input.

  • Can read data line by line or character by character.

  • Supports marking and resetting, which allows re-reading portions of the input stream.

Example

// Importing BufferedReader
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

// Creating a BufferedReader Instance
// Reading from a File
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));

// Reading from Console
// Using InputStreamReader to wrap System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

// Reading Data
// readLine(): Reads an entire line of text, returning null at the end of the file.
String line = reader.readLine();

// read(): Reads a single character and returns it as an integer (-1 indicates end of stream).
int charData = reader.read();

// Marking and Resetting
// mark(int readAheadLimit): Marks the current position in the stream.
// reset(): Resets the stream to the most recent mark.
// ready(): Checks if the stream is ready to be read.
reader.mark(100); // Marks the current position
String data = reader.readLine();
reader.reset();   // Resets to the marked position

// Skipping Characters
// skip(long n): Skips n characters in the stream.
reader.skip(5); 

package src.main.java;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Application {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Enter your age: ");
        int age = Integer.parseInt(reader.readLine());
        System.out.println("Your age is: " + age);
        reader.close();
    }
}
// Reading a File Line by Line
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// Counting Lines in a File
int lineCount = 0;
while (reader.readLine() != null) {
    lineCount++;
}

// Console Input
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ConsoleInputExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
            System.out.println("Enter your name:");
            String name = reader.readLine();
            System.out.println("Hello, " + name + "!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// Handling Exceptions
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// Read data
} catch (IOException e) {
    System.out.println("An error occurred: " + e.getMessage());
}

// Reading a config.txt file
BufferedReader configReader = new BufferedReader(new FileReader("config.txt"));
String config;
while ((config = configReader.readLine()) != null) {
    System.out.println(config);
}

Best Practices

  1. Always Close Resources: Use a try-with-resources block to automatically close the BufferedReader:

    try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
        // Read data
    }
  2. Buffer Size: Use the default buffer size unless specific performance tuning is required.

  3. Avoid Mixing with Scanner: Avoid mixing BufferedReader and Scanner in the same program for reading input to prevent unexpected behavior.

  4. Check readLine() for Null: Always check for null to handle the end of the stream gracefully.

Using Command Line Arguments

We can also pass input arguments directly when running the program from the command line. These arguments can be accessed through the args parameter in the main method.

package src.main.java;

public class Application {
    public static void main(String[] args) {
        if (args.length > 0) {
            System.out.println("First argument: " + args[0]);
        } else {
            System.out.println("No arguments provided.");
        }
    }
}

Using Console Class (Java 6 and later)

The Console class provides methods for reading input and writing output to the console. It is useful for simple console-based applications. It is a java.io package. Note that we need to execute the code via console.

package src.main.java;

import java.io.Console;

public class Application {
    public static void main(String[] args) {
        Console console = System.console();
        if (console != null) {
            String input = console.readLine("Enter your input: ");
            console.printf("You entered: %s\n", input);
        } else {
            System.out.println("Console is not available.");
        }
    }
}

Which one to use?

BufferedReader vs Scanner

Feature

BufferedReader

Scanner

Primary Purpose

Efficiently reads text from a character-based input stream in chunks.

Reads and parses input into tokens or specific data types.

Performance

Faster for reading large files due to internal buffering.

Slower for large files due to token parsing overhead.

Data Type Support

Reads text as String or characters; manual parsing required for primitives.

Built-in support for parsing primitives (int, double, etc.).

Input Sources

Can read from files, strings, or streams.

Can read from files, strings, streams, and system input (System.in).

Default Delimiter

Not applicable; reads lines or characters directly.

Whitespace (\s) by default, customizable via useDelimiter().

Line-by-Line Reading

Supports reading lines via readLine().

Possible but not as intuitive; requires custom logic.

Character-by-Character Reading

Supports reading characters using read().

Not supported directly.

End of Input Handling

Returns null for readLine() and -1 for read().

Uses hasNext() and related methods to check availability.

Parsing Capabilities

Requires manual parsing for primitives (e.g., using Integer.parseInt()).

Provides methods like nextInt(), nextDouble(), etc., for parsing.

Error Handling

Throws IOException for input/output errors.

Throws InputMismatchException for type mismatches.

Mark and Reset

Supports marking and resetting positions in the stream.

Does not support marking and resetting.

Custom Delimiters

Not applicable; processes lines/characters directly.

Supports custom delimiters via useDelimiter().

Thread Safety

Not thread-safe; external synchronization required.

Not thread-safe; external synchronization required.

Complex Input Handling

Best suited for reading raw text or large files efficiently.

Better for simple interactive inputs or structured data with tokens.

Binary File Support

Cannot handle binary files; use BufferedInputStream instead.

Not suitable for binary files.

Resource Management

Requires manual closure or use of try-with-resources.

Requires manual closure or use of try-with-resources.

Use Case

Reading and processing large text files, logs, or configuration files.

Interactive console input or small structured file parsing.

  • Use BufferedReader:

    • When working with large files where performance is critical.

    • When we need to read text line by line or character by character.

    • When parsing is not required, or we prefer manual control over parsing.

  • Use Scanner:

    • For interactive input from the console.

    • When parsing numbers, tokens, or specific data types directly is required.

    • For quick, small-scale parsing of structured input like CSVs or space-separated values.

Last updated

Was this helpful?