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:
- Reading user input from the console
- Parsing data from files
- Extracting values from strings
- Processing structured text data such as CSV
- Reading input with custom delimiter patterns
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:
- BufferedReader — More efficient for reading large text files line by line. It buffers input for better performance and is preferred when raw line reading without parsing is needed.
- Files.readString / Files.readAllLines — Simpler for reading entire small files into a single string or a list of lines in one call.
- Stream API (Files.lines) — Enables functional-style processing of file lines with operations such as filtering, mapping, and collecting.
- Console — More convenient for reading passwords
(without echoing) and formatted prompts from the console. Since Java 25,
IO.readln(prompt)is a simpler way to read a line after a prompt. - DataInputStream / ObjectInputStream — Designed for reading binary data and deserializing Java objects, not text.
- String.split / Pattern — Lighter alternatives when only simple string splitting or regex matching is needed without the overhead of a Scanner object.
Reading user input
The most common use of Scanner is reading user input from the standard
input stream (System.in).
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.
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.
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).
Bratislava 25.3 Prague 18.7 Vienna 22.1 Budapest 20.5 Warsaw 16.8
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.
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.
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.
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.
John Doe,Software Engineer,75000 Jane Smith,Data Analyst,68000 Robert Brown,DevOps Engineer,82000 Emily Davis,Product Manager,79000
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
- Use Scanner for reading typed input and parsing small, trusted, token-based text.
- Use
BufferedReaderfor large text files read line by line without parsing. - Use
Files.readString,Files.readAllLines, orFiles.linesfor simple whole-file reads and stream processing. - Remember that Scanner is not thread-safe and is slow on very large inputs.
Author
List all Java tutorials.