Data streams are used to write binary data. The DataOutputStream writes binary data of primitive types, while DataInputStream reads data back from the binary stream and converts it to primitive types. Here is an example program that writes data to a file and then reads it back into memory.
import java.io.DataInputStream import java.io.DataOutputStream import java.io.FileInputStream import java.io.FileOutputStream fun main(args : Array<String>){ val burgers = "data.burgers" //Open the file in binary mode DataOutputStream(FileOutputStream(burgers)).use { dos -> with(dos){ //Notice we have to write our data types writeInt("Bob is Great\n".length) //Record length of the array writeChars("Bob is Great\n") //Write the array writeBoolean(true) //Write a boolean writeInt("How many burgers can Bob cook?\n".length) //Record length of array writeBytes("How many burgers can Bob cook?\n") //Write the array writeInt(Int.MAX_VALUE) //Write an int for (i in 0..5){ writeByte(i) //Write a byte writeDouble(i.toDouble()) //Write a double writeFloat(i.toFloat()) //Write a float writeInt(i) //Write an int writeLong(i.toLong()) //Write a long } } } //Open a binary file in read mode. It has to be read in the same order //in which it was written DataInputStream(FileInputStream(burgers)).use {dis -> with (dis){ val bobSize = readInt() //Read back the size of the array for (i in 0 until bobSize){ print(readChar()) //Print the array one character at a time } println(readBoolean()) //Read a boolean val burgerSize = readInt() //Length of the next array for (i in 0 until burgerSize){ print(readByte().toChar()) //Print array one character at a time } println(readInt()) //Read an int for (i in 0..5){ println(readByte()) //Read a byte println(readDouble()) //Read a double println(readFloat()) //Read a float println(readInt()) //Read an int println(readLong()) //Read a long } } } }
The program creates a FileOutputStream object and passes the name of the file to its constructor. The FileOutputStream object is then passed to the constructor of DataOutputStream. We apply the use() function to ensure all resources are freed properly when we have finished. The file is now open for writing in binary mode.
When we wish to use the same object repeatedly, we can pass it to the with() function. In our case, we intend to keep using our DataOutputStream object, so on line 11, we pass it to the with() function. Inside of the with() function, all method calls will target the dos object because it was supplied to with().
Since we intend to write a string to the file, we need to record the length of the string. We do this using the writeInt function and passing the length of our string to it. Then we can use writeChars() to write a character array to the file. The String argument is converted to a character array and written to the file. Finally, we call writeBoolean to write true/false values to the file.
The next section is a repeat of the first. We intend to write another string to the file, but do so, we need to record the length of the file. Once again, we turn to writeInt() to record an int value. The next line, we use writeBytes() rather than writeChars() to demonstrate how we can write a byte array rather than a String. The DataOutputStream class sees to the details of turning a String into a byte array. Finally, we write another int value to the stream.
Next, we enter a for loop on line 21. Inside of the for loop, we demonstrate writing different primitive types to the file. We can use writeByte() for a byte, writeDouble() for a double, and so on for each primitive type. The DataOutputStream class knows the size of each primitive type and writes the correct number of bytes for each primitive.
When we are done writing the object, we open it again to read it. Line 33 creates a FileInputStream object that accepts the path to the file in its constructor. The FileInputStream object is chained to DataInputStream by passing it to the constructor of DataInputStream. We apply the use() function to ensure all resources are properly closed.
Reading the file requires the file to be read in the same order in which it is written. Our first order of business is to grab the size of the character array we wrote to the file earlier. We use readInt() on line 35 followed by a for loop that terminates at the size of the array on line 36. Each iteration of the for loop calls readChar() and the String is printed to the console. When we are finished, we read a boolean on line 39.
Our next array was a byte array. Once again, we need it’s final size so we call readInt() on line 41. Lines 42-44 run through the array and call readByte() until the loop terminates. Each byte is converted to a character object using toChar(). On line 45, we read an int using readInt().
The final portion of the program repeats the for loop found earlier. In this case, we enter a for loop that terminates after five iterations (line 47). Inside of the for loop, we call readByte(), readDouble(), readFloat(), and so on. Each call prints the restored variable to the console.