Sometimes, it isn't enough to use readln() to read data from standard input. readln() is a simple method, not suitable for complex tasks, for example: find only Integers or only Doubles in a data flow, read data sequentially, word by word, and control such reading step by step. In this topic, you will learn to use one of Java tools that can be also used in Kotlin — Scanner.
What is Java Scanner?
Scanner is another way to obtain data from standard input. You can access it directly from Kotlin because it is interoperable with other Java libraries. Scanner allows a program to read values of different types (strings, numbers, etc.) from standard input.
To use it, you need to add the following import statement to the top of your file with the source code:
import java.util.Scanner
or
import java.util.*
You can use either method to add Scanner to your program. We will discuss importing in more detail later.
Let's create a variable initialized by Scanner:
val scanner = Scanner(System.`in`)
In this line, System.`in` is an object that represents the standard input stream. scanner wraps it as an internal data source and provides a set of convenient methods to work with it. Sure, you can use Scanner(System.`in`) in your code instead of creating a scanner variable, but using a single variable is a more convenient and readable way to do it.
Now we can read data from standard input:
val line = scanner.nextLine() // read a whole line, e.g., "Hello, Kotlin"
val num = scanner.nextInt() // read a number, e.g., 123
val string = scanner.next() // read a string, e.g., "Hello"
scanner.next() reads only one word, not a line. If the user enters Hello, Kotlin, it will read Hello,.
After you call scanner.nextLine() or scanner.nextInt() or something similar, the program will anticipate input data. Here is an example of correct input data:
Hello, Kotlin
123 Hello
The input example below is also valid:
Hello, Kotlin
123
Hello
It's possible to read a number as a string using scanner.next() or scanner.nextLine() if the number is on a new line.
Also, the Scanner type provides several methods (functions) for reading values of other types. Refer to the Class Scanner documentation for more details.
The program below reads two numbers and outputs them in reverse order on two different lines:
import java.util.Scanner // a class (type) from the Java standard library
fun main() {
val scanner = Scanner(System.`in`) // reads data
val num1 = scanner.nextInt() // reads the first number
val num2 = scanner.nextInt() // reads the second number
println(num2) // prints the second number
println(num1) // prints the first number
}
Ok, now we've read the data and don't need Scanner anymore. When we used readln(), we just proceeded with our code and moved further. But Scanner can consume PC resources all the time. To stop it, we need to use the method close(), which... cancels Scanner:)
scanner.close()
Now we are good.
Custom delimiter
One important thing: we can input data directly in Scanner. Let's see:
val scanner = Scanner("123_456")
But how can we read next word if we don't have whitespaces or new lines? For these cases, Scanner has a convenient tool — useDelimiter(). With its help, we can customize our delimiters for methods like next(), nextInt(), etc. (the default value of delimiter is a whitespace).
To read these words and integers separately, we have to set a new delimiter symbol – "_":
scanner.useDelimiter("_")
Now, we can read this data:
println(scanner.nextInt()) // 123
println(scanner.nextInt()) // 456
Cool!
Check next element
Suppose we have the following data on our Scanner:
val scanner = Scanner("Hello, Kotlin!")
Now, we try to read several words:
val word1 = scanner.next()
val word2 = scanner.next()
val word3 = scanner.next()
When we try to read the third word, we'll receive an exception and our program will crash. What can we do with that? Huh, Scanner has its own method for such a situation – it is called hasNext() (hasNextInt(), etc).
if (scanner.hasNext()) val word1 = scanner.next() // Hello,
if (scanner.hasNext()) val word2 = scanner.next() // Kotlin!
if (scanner.hasNext()) val word3 = scanner.next() // It doesn't work but also doesn't make error
Now, we are able to read data or not to read it if our data flow has ended.
Conclusion
In this topic, we have discussed the second way to read from standard input — Java Scanner. It is a convenient way to read complex data and filter content. Here's what you need to remember:
If
Scannerisn't needed, always usereadln().To read different types of data, Scanner has methods like
next(),nextInt(), andnextLine().If you have input with strange delimiters, use the method
useDelimiter().If you don't know how many words the input has, use the method
hasNext().