ZetCode

Java Scanner

last modified October 11, 2026

In this article we show how to read and parse input in Java using java.util.Scanner.

The Scanner class in Java is a simple text scanner that can parse primitive types and strings using regular expressions. It breaks its input into tokens using a delimiter pattern, which by default matches whitespace.

The Scanner class is commonly used for:

The scanner can read from various input sources including InputStream, File, Path, Readable, and String.

The examples are written as compact source files with an instance main method, so they require Java 25 or later. Save each example as Main.java and run it with java Main.java.

Scanner parses numbers using the default locale of the system. The examples assume a dot as the decimal separator, as in US English. On a system that uses a comma, nextDouble would fail on 3.14. The locale section below shows how to control this.

While Scanner is versatile, other classes may be better suited for specific tasks:

Reading user input

The most common use of Scanner is reading user input from the standard input stream (System.in).

Main.java
void main() {

    var scanner = new Scanner(System.in);

    System.out.print("Enter your name: ");
    var name = scanner.nextLine();

    System.out.print("Enter your age: ");
    var age = scanner.nextInt();

    System.out.printf("Hello %s, you are %d years old%n", name, age);
}

The nextLine method reads a line of text, while nextInt reads an integer value. The Scanner automatically handles the conversion from text to the requested type.

Don't close a scanner that wraps System.in before the program has finished reading input. Closing it also closes the standard input stream, so later reads fail. You don't need to close it at the end either, since the JVM releases the stream when the program exits. That is why the stdin examples above don't use try-with-resources.

$ java Main.java
Enter your name: John
Enter your age: 34
Hello John, you are 34 years old

Reading different data types

The Scanner provides methods for reading various primitive types and common data types. Each method has a corresponding hasNextXxx method for checking availability.

Main.java
void main() {

    var scanner = new Scanner(System.in);

    System.out.print("Enter an integer: ");
    if (scanner.hasNextInt()) {
        var i = scanner.nextInt();
        System.out.printf("Integer: %d%n", i);
    }

    System.out.print("Enter a double: ");
    if (scanner.hasNextDouble()) {
        var d = scanner.nextDouble();
        System.out.printf("Double: %.2f%n", d);
    }

    System.out.print("Enter a boolean: ");
    if (scanner.hasNextBoolean()) {
        var b = scanner.nextBoolean();
        System.out.printf("Boolean: %b%n", b);
    }

    System.out.print("Enter a word: ");
    var token = scanner.next();
    System.out.printf("Token: %s%n", token);
}

The hasNextInt, hasNextDouble, and hasNextBoolean methods check whether the next token can be interpreted as the requested type, preventing InputMismatchException.

$ java Main.java
Enter an integer: 42
Integer: 42
Enter a double: 3.14
Double: 3.14
Enter a boolean: true
Boolean: true
Enter a word: hello
Token: hello

The nextInt and nextLine pitfall

The nextInt method reads the number but leaves the line terminator in the input. A following nextLine then returns an empty string, which is one of the most common Scanner bugs.

Main.java
void main() {

    var data = "42\nJohn Doe\n";

    try (var scanner = new Scanner(data)) {

        var age = scanner.nextInt();

        // consume the rest of the first line before reading the name
        scanner.nextLine();
        var name = scanner.nextLine();

        System.out.printf("%d %s%n", age, name);
    }
}

Without the first nextLine call, the variable name would be an empty string, because the call would return the remainder of the line that holds the number.

$ java Main.java
42 John Doe

Reading from a file

The Scanner can read input directly from a file, line by line or token by token. The new File(...) path is resolved against the working directory, and the constructor uses the default charset. For files with non-ASCII text, pass the charset explicitly, for example new Scanner(Path.of("temperatures.txt"), StandardCharsets.UTF_8).

temperatures.txt
Bratislava 25.3
Prague 18.7
Vienna 22.1
Budapest 20.5
Warsaw 16.8
Main.java
void main() throws Exception {

    try (var scanner = new Scanner(new File("temperatures.txt"))) {

        while (scanner.hasNextLine()) {
            var line = scanner.nextLine();
            System.out.println(line);
        }
    }
}

The hasNextLine method checks if there is another line in the input. The try-with-resources statement ensures that the scanner is properly closed after use.

$ java Main.java
Bratislava 25.3
Prague 18.7
Vienna 22.1
Budapest 20.5
Warsaw 16.8

Parsing structured file data

We can combine token-reading methods with line-reading to parse structured data from files.

Main.java
void main() throws Exception {

    try (var scanner = new Scanner(new File("temperatures.txt"))) {

        while (scanner.hasNext()) {

            var city = scanner.next();
            var temperature = scanner.nextDouble();

            System.out.printf("%s: %.1f°C%n", city, temperature);
        }
    }
}

The next method reads the city name as a string token, and nextDouble reads the temperature value. The default whitespace delimiter separates the tokens correctly. This approach assumes that each city name is a single word. A name such as New York would be split into two tokens and break the pairing of names and temperatures.

$ java Main.java
Bratislava: 25.3°C
Prague: 18.7°C
Vienna: 22.1°C
Budapest: 20.5°C
Warsaw: 16.8°C

Reading from a string

The Scanner can also parse data directly from a string, which is useful for processing text data embedded in the application.

Main.java
void main() {

    var data = "apple 1.20 banana 0.80 cherry 2.50";

    try (var scanner = new Scanner(data)) {

        while (scanner.hasNext()) {

            var fruit = scanner.next();
            var price = scanner.nextDouble();

            System.out.printf("%s: $%.2f%n", fruit, price);
        }
    }
}

The string is parsed token by token. The Scanner iterates through the text, extracting alternating string and double values.

$ java Main.java
apple: $1.20
banana: $0.80
cherry: $2.50

Custom delimiters

The useDelimiter method sets a custom delimiter pattern for tokenizing the input. This is useful for parsing non-whitespace separated data.

Main.java
void main() {

    var data = "10,20,30,40,50";

    try (var scanner = new Scanner(data)) {

        scanner.useDelimiter(",");

        var sum = 0;

        while (scanner.hasNextInt()) {
            sum += scanner.nextInt();
        }

        System.out.printf("Sum: %d%n", sum);
    }
}

The delimiter is set to a comma with useDelimiter(","), allowing the Scanner to parse comma-separated integer values. A delimiter can also be a regular expression, such as "\\s*,\\s*", which ignores spaces around the commas. A plain "," does not match newlines, so input that spans several lines needs a pattern such as "[,\\n]".

$ java Main.java
Sum: 150

Reading CSV data

A more realistic CSV parsing example demonstrates reading structured tabular data from a file.

employees.csv
John Doe,Software Engineer,75000
Jane Smith,Data Analyst,68000
Robert Brown,DevOps Engineer,82000
Emily Davis,Product Manager,79000
Main.java
void main() throws Exception {

    try (var scanner = new Scanner(new File("employees.csv"))) {

        while (scanner.hasNextLine()) {

            var line = scanner.nextLine();

            try (var lineScanner = new Scanner(line)) {
                lineScanner.useDelimiter(",");

                var name = lineScanner.next();
                var position = lineScanner.next();
                var salary = lineScanner.nextInt();

                System.out.printf("%-20s %-20s $%d%n",
                        name, position, salary);
            }
        }
    }
}

Each line is read with nextLine, then a secondary Scanner parses the comma-separated fields. This nested approach is easy to adapt for different formats. It does not handle quoted fields or commas inside values; for full CSV support use a dedicated CSV library. For simple lines without quotes, a plain line.split(",") is lighter, since it avoids creating a Scanner for each line.

$ java Main.java
John Doe             Software Engineer    $75000
Jane Smith           Data Analyst         $68000
Robert Brown         DevOps Engineer      $82000
Emily Davis          Product Manager      $79000

Finding patterns with findInLine

The findInLine method searches for a pattern in the current line without advancing past the line terminator.

Main.java
void main() {

    var data = """
            Name: John Doe  Age: 34  City: Bratislava
            Name: Jane Doe  Age: 28  City: Prague
            """;

    try (var scanner = new Scanner(data)) {

        while (scanner.hasNextLine()) {

            var name = scanner.findInLine("Name: (\\w+\\s+\\w+)");
            var age = scanner.findInLine("Age: (\\d+)");
            var city = scanner.findInLine("City: (\\w+)");

            if (name != null) {
                System.out.printf("%s, %s, %s%n",
                        name.replace("Name: ", ""), age, city);
            }

            if (scanner.hasNextLine()) {
                scanner.nextLine();
            }
        }
    }
}

The findInLine method applies a regular expression to the current line and returns the matched text, or null if no match is found.

$ java Main.java
John Doe, Age: 34, City: Bratislava
Jane Doe, Age: 28, City: Prague

Using Locale-aware scanning

The useLocale method configures locale-specific formatting for parsing numbers, such as decimal separators.

Main.java
void main() {

    var data = "3.14 2.718 1,5";

    try (var scanner = new Scanner(data)) {

        // US locale uses dot as decimal separator
        scanner.useLocale(Locale.US);
        System.out.printf("US: %.2f%n", scanner.nextDouble());
        System.out.printf("US: %.2f%n", scanner.nextDouble());

        // German locale uses comma as decimal separator
        scanner.useLocale(Locale.GERMAN);
        System.out.printf("DE: %.2f%n", scanner.nextDouble());
    }
}

The Locale.US interprets the dot as a decimal separator, while Locale.GERMAN uses the comma. This is important when parsing internationalized data. Note that the US locale treats a comma as a thousands separator, so an input such as 2,718 would be read as 2718 rather than 2.718. The reverse holds for the German locale: with it, 2.718 would be read as 2718, since the dot is a grouping separator there.

Scanner uses the default locale of the system unless told otherwise. On a system with a comma decimal separator, a value such as 3.14 would fail to parse with nextDouble, so calling useLocale makes the parsing explicit.

$ java Main.java
US: 3.14
US: 2.72
DE: 1.50

The skip method

The skip method skips input that matches a given pattern, useful for ignoring headers or unwanted sections.

Main.java
void main() {

    var data = """
            === Data Start ===
            10 20 30
            40 50 60
            === Data End ===
            """;

    try (var scanner = new Scanner(data)) {

        // skip the header line
        scanner.skip("=== Data Start ===\\s*");

        while (scanner.hasNextInt()) {
            var value = scanner.nextInt();
            System.out.printf("%d ", value);
        }

        System.out.println();
    }
}

The skip method discards the header line, allowing the Scanner to proceed directly to parsing the numeric data.

$ java Main.java
10 20 30 40 50 60

Reading with radix

The useRadix method sets the default radix for interpreting numeric values. This is useful for parsing hexadecimal, octal, or binary numbers.

Main.java
void main() {

    var hexData = "FF A0 1C 4B";

    try (var scanner = new Scanner(hexData)) {

        scanner.useRadix(16);

        while (scanner.hasNextInt()) {
            var value = scanner.nextInt();
            System.out.printf("%d ", value);
        }

        System.out.println();
    }
}

With radix set to 16, the Scanner interprets hexadecimal values such as FF as decimal 255.

$ java Main.java
255 160 28 75

Handling InputMismatchException

When input does not match the expected type, the Scanner throws InputMismatchException. Proper exception handling ensures robust applications.

Main.java
void main() {

    var data = "42 abc 3.14";

    try (var scanner = new Scanner(data)) {

        while (scanner.hasNext()) {

            try {
                System.out.printf("Int: %d%n", scanner.nextInt());
            } catch (InputMismatchException e) {
                System.out.printf("Not an integer: %s%n", scanner.next());
            }
        }
    }
}

The nextInt method throws the exception when the next token is not an integer. The token is not consumed by the failed call, so the catch block can skip it with next. Without that call, a retry loop around nextInt would never finish, because it would keep failing on the same token:

// broken: retries forever on "abc"
while (true) {
    try {
        var n = scanner.nextInt();
        break;
    } catch (InputMismatchException e) {
        System.out.println("Not a number");
    }
}

// fixed: discard the bad token before retrying
while (!scanner.hasNextInt()) {
    System.out.println("Not a number");
    scanner.next();
}
var n = scanner.nextInt();

The safest approach is to use hasNextXxx methods before calling the corresponding nextXxx methods, avoiding exceptions altogether. Also note that Scanner does not throw IOException from its methods. When reading fails, it stores the exception, which you can check with ioException().

$ java Main.java
Int: 42
Not an integer: abc
Not an integer: 3.14

Scanner tokens and streams

The tokens method returns a stream of tokens, enabling functional-style processing of scanner input.

Word frequency counter

The following example tokenizes a sentence and counts how many times each word appears, using Collectors.groupingBy with Collectors.counting.

Main.java
void main() {

    var data = "the sun is bright the moon is pale the grass is green";

    try (var scanner = new Scanner(data)) {

        scanner.tokens()
                .map(String::toLowerCase)
                .collect(Collectors.groupingBy(
                        word -> word, TreeMap::new, Collectors.counting()))
                .forEach((word, count) ->
                        System.out.printf("%-12s %d%n", word, count));
    }
}

The tokens method converts the scanner's input into a Stream<String>. We map each token to lowercase and collect the stream into a TreeMap that groups words and counts their occurrences. The result is printed in alphabetical order.

$ java Main.java
bright       1
grass        1
green        1
is           3
moon         1
pale         1
sun          1
the          3

Filtering and transforming tokens

In this example we parse city-temperature pairs and filter cities whose temperature exceeds 20°C, format the output, and display them in sorted order.

Main.java
void main() {

    var data = "Bratislava 25.3 Prague 18.7 Vienna 22.1 Budapest 20.5";

    try (var scanner = new Scanner(data)) {

        var tokens = scanner.tokens().toList();

        var warmCities = IntStream.range(0, tokens.size())
                .filter(i -> i % 2 == 1)
                .filter(i -> Double.parseDouble(tokens.get(i)) > 20)
                .mapToObj(i -> "%s: %.1f\u00b0C".formatted(
                        tokens.get(i - 1), Double.parseDouble(tokens.get(i))))
                .sorted()
                .toList();

        warmCities.forEach(System.out::println);
    }
}

We collect all tokens into a list, then use IntStream.range to work with index positions. The odd indices contain temperatures; we filter those above 20, format city-temperature pairs, and sort the results.

$ java Main.java
Bratislava: 25.3°C
Budapest: 20.5°C
Vienna: 22.1°C

Grouping and summarizing numeric data

This example reads alternating department-salary pairs and computes the average salary for each department.

Main.java
void main() {

    var data = "IT 45000 IT 52000 HR 38000 HR 41000 IT 49000 HR 39500";

    try (var scanner = new Scanner(data)) {

        var tokens = scanner.tokens().toList();

        var depts = new TreeMap<>(IntStream.range(0, tokens.size())
                .filter(i -> i % 2 == 0)
                .boxed()
                .collect(Collectors.groupingBy(
                        i -> tokens.get(i),
                        Collectors.averagingDouble(
                                i -> Double.parseDouble(tokens.get(i + 1))))));

        depts.forEach((dept, avg) ->
                System.out.printf("%s: $%.2f%n", dept, avg));
    }
}

Even indices hold department names, odd indices hold salaries. We filter for even indices, group by department name, and compute the average of the adjacent salary values using Collectors.averagingDouble.

$ java Main.java
HR: $39500.00
IT: $48666.67

Building a configuration map

Here we parse key-value pairs from a string and collect them into a Map using Collectors.toMap.

Main.java
void main() {

    var data = "host localhost port 5432 user admin timeout 30";

    try (var scanner = new Scanner(data)) {

        var tokens = scanner.tokens().toList();

        var config = IntStream.range(0, tokens.size() - 1)
                .filter(i -> i % 2 == 0)
                .boxed()
                .collect(Collectors.toMap(
                        i -> tokens.get(i),
                        i -> tokens.get(i + 1),
                        (a, b) -> a,
                        TreeMap::new));

        config.forEach((key, value) ->
                System.out.printf("%s = %s%n", key, value));
    }
}

Even indices contain keys and odd indices contain values. We use IntStream.range to iterate indices, filter for even positions, and collect the adjacent pairs into a map. The TreeMap::new supplier keeps the keys sorted, so the output order is predictable. A plain HashMap would print the entries in an arbitrary order.

$ java Main.java
host = localhost
port = 5432
timeout = 30
user = admin

Source

Java Scanner - language reference

In this article we have used Java's Scanner class to read and parse input data. To summarize:

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.