The BufferedReader and BufferedWriter classes improve the performance of reading and writing operations by adding an in-memory buffer to the streams. By using a memory buffer, the program and reduce the number of calls required to the underlying read and write streams and thus improve performance. Here is an example program that makes use of both BufferedReader and BufferedWriter.
fun main(args : Array<String>){ when (args.size){ //Check for two command line arguments 2 -> { //Grab source and destination files val src = args[0] val dest = args[1] //Check if the destination file exists. We can create it //if needed with (File(dest)){ if(!exists()){ createNewFile() } } //Now, open the source file in read mode. The BufferedReader //provides buffering to improve performance BufferedReader(FileReader(src)).use { reader -> //Likewise, open the destination file in write mode //The BufferedWriter class provides buffering for performance BufferedWriter(FileWriter(dest)).use { writer -> //Read through the source file one character at a time var character = reader.read() while(character != -1){ //Write the character to the destination file writer.write(character) //Read the next character. character = reader.read() } } } } else -> { println("Source file followed by destination file required") System.exit(-1) } } }
The example program copies the source file to the destination file. We begin by using the when() function to check if we have two and only two command line arguments. If we have a source and destination file, the program continues starting on line 6 otherwise it jumps down to line 39 and exits after printing an error message.
On lines 6 and 7, we grab our source and destination files from the command line parameters. On line 11, we create a new File object and pass it to the with() function to see if we need to make a new file for the destination. Line 12 uses the exits() property to see if the file exists, and if it doesn’t exist, line 13 creates the new file.
Starting at line 19, we open the source file and begin our copy operation. The file is opened by creating a new FileReader object and passing in the name of the source file. The FileReader object is then passed to the constructor of BufferedReader. We utilize the use() function to ensure that all resources are properly closed when we are finished with the read operation. It’s also worth noting that we call the lambda parameter reader rather than it to improve code readability.
Line 23 opens the destination file for writing. We create a FileWriter (the companion object to FileReader) and pass the name of the destination file to the FileWriter’s constructor. The FileWriter object is passed to the BufferedWriter constructor to provide buffering support. Once again, we utilize the use() function to ensure that all resources are closed when finished.
The copy operation is fairly anti-climatic. We read the first character on line 26 and then enter into a while loop that terminates when character == -1. Inside of the while loop, we write the character to the destination file (line 30) and then read the next character (line 33). The use() function that was applied to both the BufferedReader and BufferedWriter objects closes the files when finished.
The program can be run by using the following commands at the command line.
kotlinc BufferedCopy.kt -include-runtime -d BufferedCopy.jar ava -jar BufferedCopy.jar [dest file]
When finished, the dest file will be an exact copy of source file.