Skip to main content

Java I/O operations & streams : Read, Write from file

A stream is a sequence of data of different types like simple bytes, primitive data types and objects. Input Stream is used to read data from a source one at a time and Output Stream is used to write data to a destination one at a time.

Byte Streams 
Byte Streams are used to input and output 1 byte (i.e. 8 bits) at a time. Byte Stream classes for input and output are extended from java.io.InputStream and java.io.OutputStream abstract classes.

FileInputStream and FileOutputStream are the commonly used classes for dealing with file read and write operations, which reads or writes data one byte at a time.

Let's take an example, where we have an input file with data "Year is 2018". Below program will read one byte at a time and write in a new output file.
package com.company;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        FileInputStream fin = null;
        FileOutputStream fout = null;
        try {
            fin = new FileInputStream("files/input.txt");
            fout = new FileOutputStream("files/output.txt");
            int b;
            while((b = fin.read()) != -1) {
                System.out.println(b);
                fout.write(b);
            }
        }
        finally {
            if (fin != null) {
                fin.close();
            }
            if (fout != null) {
                fout.close();
            }
        }
    }
}


If you look at the code, you will notice that read() method from FileInputStream returns an integer in Java because a byte can have a range of 0-255 (1 Byte = 8 bits i.e. 2^8 values). Bytes data type in Java has a range -128 to 127 and integer has a range from 0 to 255, so Integers make more sense to represent this data.

I would also like to highlight that even though we will have characters in our input file, but since the returned data is integer representation of data, System.out.println() will actually print the output as integer but the write() method will automatically do the casting while writing the file and we will get the identical files. To get the same result in print statement, we can type-cast the data to char i.e. use the following statement
  System.out.println((char) b);

Character Streams 
Java stores characters in unicode format and the conversion is done automatically by I/O character stream. Character Stream classes for input and output are extended from java.io.Reader and java.io.Writer abstract classes.

FileReader and FileWriter are the commonly used classes for dealing with file read and write operations, which reads or writes data one character at a time.

Code for reading characters is similar to reading bytes, i.e. integer is used to read data but the main difference is that for character streams, integer variable holds its value in last 16 bits while for byte reading, the value is stored in last 8 bits. Character stream can support all types of character sets ASCII, Unicode, UTF-8, UTF-16 etc while Byte stream is more suitable for ASCII set. Character streams are capable to translate implicitly 8-bit data to 16-bit data and vice-versa.

package com.company;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        FileReader fin = null;
        FileWriter fout = null;
        try {
            fin = new FileReader("files/input.txt");
            fout = new FileWriter("files/output.txt");
            int c;
            while((b = fin.read()) != -1) {
                System.out.println((char) c);
                fout.write(c);
            }
        }
        finally {
            if (fin != null) {
                fin.close();
            }
            if (fout != null) {
                fout.close();
            }
        }
    }
}


Buffered Streams 
Above two examples are unbuffered i.e. every time a read or write is done, underlying OS call is triggered which includes disk access, network activity which is not very efficient and expensive. Java supports buffered streams which reads and writes data to buffer and the OS API is called only when the buffer is empty (for input stream) and when buffer is full (for output stream).

BufferedReader and BufferedWriter classes are used to read and write data. In the code below, we are using readLine method which can be used to read the entire line.
package com.company;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        BufferedReader fin = null;
        BufferedWriter fout = null;
        try {
            fin = new BufferedReader(new FileReader("files/input.txt"));
            fout = new BufferedWriter(new FileWriter("files/output.txt"));
            String s;
            while((s = fin.readLine()) != null) {
                System.out.println(s);
                fout.write(s + "\n");
            }
        }
        finally {
            if (fin != null) {
                fin.close();
            }
            if (fout != null) {
                fout.close();
            }
        }
    }
}
Scanning
Scanner class can also be used to read data from a file which is useful for breaking down formatted input into tokens. Scanner class uses white-space as a delimiter by default.
package com.company;

import java.io.*;
import java.util.Scanner;

public class Test {

    public static void main(String[] args) throws IOException {
        Scanner s = null;
        try {
            s = new Scanner(new BufferedReader(new FileReader("files/input.txt")));
            while(s.hasNext()) {
                System.out.println(s.next());
            }
        }
        finally {
            if (s != null) {
                s.close();
            }
        }
    }
}

Comments