Java IO Character Streams Tutorial

1- Introduction

In the previous instruction document, I introduced input-output binary stream. You need to understand it before studying input-output character stream. You can see here:

2- The difference between binary streams and character streams

Binary Stream, each one reads a byte (equivalent to 8 bits)
Meanwhile, character stream read a character in each reading turn. It is dependent on type of encoding (UTF-8, UTF-16,...) to decide the number of bytes in each reading turn which are 1 byte, 2 bytes, or 3 bytes. Let's see the following illustrated image:

UTF-16:

This is a Japanese text. If it is stored in a File encoded UTF-16, bytes in hard disk drive will be similar to the illustrated image:
  • Two first bytes (254,255) mean notifying the beginning of a series of UTF-16 encoding.
  • The next characters are encoded by 2 bytes.
    • For example, J character is encoded with 2 bytes (0 and 74)
    • P character is encoded with 2 byte (0 and 80)
    • .....
  • When reading from file with UTF-16 encoding, it will exclude two first bytes and read two successive bytes.

UTF-8:

It will be different if the same above-mentioned Japanese text is recorded with  UTF-8 encoding. You can see that bytes are stored in hard disk drive:
  • ​To normal latin characters, it takes only 1 byte to store.
    • For example, it takes 1 byte to store J character (74)
    • It takes 1 byte to store P character (80)
  • It may take 2 bytes or 3 bytes to store other characters.
  • In the rule of reading, it has a ** table of standard UTF8 characters
    • Read the first byte, if it is <= 127, it will be a ASII character.
    • On the contrary, if it is >127, it will need to continue to read the second byte and consider whether those two bytes are 1 character or not, and what character it is.
    • If in the previous step, it does not have a character, it will carry on reading the third byte and join all of them into a character.

To sum up, when you save a document with any encoding, you need to read with an equivalent encoding, or else the output of reading will be wrong.

3- Overview of the character streams

This is the class hierarchy of character streams:

4- Class java.io.Reader

Reader is a abstract class. Reading character streams are extended from this class.
Create a file test_reader.txt to start with Reader example:
  • HelloReader.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class HelloReader {

   public static void main(String[] args) throws IOException {
      
       // Character stream, read a file
       // FileReader read file with default encoding of machine running this code.
       Reader r = new FileReader("test_reader.txt");
       int i = -1;

       // Read one character (return int)
       while ((i = r.read()) != -1) {
           // Cast to char.
           System.out.println((char) i);
       }
       r.close();
   }

}
Kết quả chạy ví dụ:
The next example is to read many characters in a reading turn, and it is assigned on a temporary array. This helps enhance the efficiency of the program compared to reading each character in turn.
  • HelloReader2.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

// This example, read multi characters in once.
public class HelloReader2 {

    public static void main(String[] args) throws IOException {

      
        // Character stream, read a file
        // FileReader read file with default encoding of machine running this code.        
        Reader r = new FileReader("test_reader.txt");

        // Create temporary array of characters.
        char[] temp = new char[10];
        int i = -1;

     
        // Method read(char[]):
        // Reads characters into an array.
        // The number of characters read.
        // or -1 if the end of the stream has been reached
        while ((i = r.read(temp)) != -1) {
            String s = new String(temp, 0, i);
            System.out.println(s);
        }
        r.close();

    }

}

5- Class java.io.Writer

Writer Class is a abstract class. All of output character streams are extended from this class. 
  • HelloWriter.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class HelloWriter {

    public static void main(String[] args) throws IOException {
        File dir = new File("C:/test");

        // Create directories, if it not exists.
        dir.mkdirs();


        // Create character stream to write to file.
        // Using default encoding of machine running this code.     
        Writer w = new FileWriter("C:/test/test_writer.txt");

        // Array of characters.
        char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'r',
                'i', 't', 'e', 'r' };

        // Write characters to stream.
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];

            // Cast to int.
            int j = (int) ch;

            // Write to stream.
            w.write(j);
        }

        // Close stream,
        w.close();
    }
}
Results of running the example:
The next example is to write many characters in a stream at the same time. Specifically, we write a array of characters in a stream. This helps enhance the efficiency of the program compared to writing each character in turn.
  • HelloWriter2.java
package org.o7planning.tutorial.javaio.readerwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class HelloWriter2 {
    
    public static void main(String[] args) throws IOException {

        File dir = new File("C:/test");

        // Create directories, if it not exists.
        dir.mkdirs();

        // Create character stream to write file.
        Writer w = new FileWriter("C:/test/test_writer2.txt");


        char[] chars = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'r',
                'i', 't', 'e', 'r' };


        // Write characters to stream.
        w.write(chars);
       
        // Typically Java cache used to store data (in memory)
        // when the buffer is full, it pushes the data to the file.
        // You can actively push data into the file.
        w.flush();
        
        // Write 'new line' character to stream.s
        w.write('\n');

        String s = "FileWriter";

        // Write a String to stream.
        w.write(s );
        
    
        // Close stream.
        // It will push the data in buffer to the file, and close stream.
        // Finish write file.
        w.close();
    }
}
Results of running the example:

6- How a binary stream is converted into a character stream?

You have a binary stream. And you want to convert it into a character stream?
In the above examples, we get accustomed with Reader and Writer. The next example allows you actively to read and write in the stream with a clearly specified encoding.
Create file test_utf8.txt
  • test_utf8.txt
JP日本-八洲
When you save, eclipse will ask you what type of encoding you want to save. Select UTF-8
  • InputStreamReaderExample.java
package org.o7planning.tutorial.javaio;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class InputStreamReaderExample {

    public static void main(String[] args) throws IOException {

        // Create binary stream, read a file.
        InputStream in = new FileInputStream("test_utf8.txt");


        // Create character stream from binary stream.
        // encoding UTF-8
        Reader reader = new InputStreamReader(in, "UTF-8");

        int i = 0;

        // Read turn each character
        while ((i = reader.read()) != -1) {

            // cast int to char, and print to the Console
            System.out.println((char) i + " " + i);
        }
        reader.close();
    }
}
Results of running the example
The next example write file encoded with UTF-8
  • OutputStreamWriterExample.java
package org.o7planning.tutorial.javaio;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class OutputStreamWriterExample {

    public static void main(String[] args) throws IOException {
        File dir = new File("C:/test");

        // Create directories if it not exists.
        dir.mkdirs();

        // Create binary output stream, write to a file.
        OutputStream out = new FileOutputStream("C:/test/test_write_utf8.txt");

        // Create character stream from binary stream.
        // encoding UTF-8.
        Writer writer = new OutputStreamWriter(out, "UTF-8");

        String s = "JP日本-八洲";
        writer.write(s);
        writer.close();
    }

}
Results of running the example

7- Class java.io.BufferedReader

// If you want to read each line of data of a text file. BufferedReader is a good choice.
// As a direct subclass of the Reader class.
// Constructor
public BufferedReader(Reader in);

// Create BufferedBuffer object, wrap Reader object.
// Utility methods of BufferedReader :
// Read a line.
public String readLine();

// The code example:
// Create stream read a file.
Reader r=new FileReader("C:/test.txt");
BufferedReader br=new BufferedReader(r);

// The code example:
InputStream in = new FileInputStream("C:/test.txt");
Reader r = new InputStreamReader(in, "UTF-8");
BufferReader br = new BufferedReader(r);
If you want to read each line of data of a text file.  BufferedReader is a good choice.
  • test_multi_lines.txt
## Fruit List
Apricots
Barbados Cherries
Bitter Melon
Cherimoya
Honeydew
Jackfruit
Limes
Lychee
Mango
Oranges
Pineapple
Strawberries
  • BufferedReaderExample.java
package org.o7planning.tutorial.javaio.buffered;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class BufferedReaderExample {

    public static void main(String[] args) throws IOException {

        InputStream in = new FileInputStream("test_multi_lines.txt");
        Reader reader = new InputStreamReader(in, "UTF-8");
        BufferedReader br = new BufferedReader(reader);

        String s = null;
        int i = 0;
       
        // Read each line of data
        // If returns null means ending stream.
        while ((s = br.readLine()) != null) {
            i++;
            System.out.println(i + " : " + s);
        }
        br.close();
    }

}
Results of running the example:

8- Class java.io.BufferedWriter

// BufferedWriter class is a direct subclass of Writer.
// Constructor
// Create BufferedWriter object, wrap other Writer object.
public BufferedWriter(Writer out);


// Methods of BufferedWriter.
// Equivalent to calling write ('\ n');
public String newLine();

// The code example:
// Create character stream to write file.
Writer w=new FileWriter("C:/jhelp/test_bufferedWriter.txt");
// Create BufferedWriter wrap 'w'
BufferedWriter bw=new BufferedWriter(w);
bw.write("Hello..");
// Print 'new line'
bw.newLine();

9- Class java.io.FilterReader

FilterReader which is a stream reading character read selectively required characters. For example, you want to read a text file containing HTML tags and exclude characters in tag. You need to write a subclass of  FilterReader and then use that subclass. You cannot directly use  FilterReader since it is a abstract class.
For example, a stream reads filtered characters, read  HTML but ignore characters in tag. 
For example, input  "<h1>Hello</h1>" ==> output:   "Hello".
  • RemoveHTMLReader.java
package org.o7planning.tutorial.javaio.filter;

import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;

public class RemoveHTMLReader extends FilterReader {

  // Used to remember whether we are "inside" a tag
  boolean intag = false;

  public RemoveHTMLReader(Reader in) {
      super(in);
  }

  /**
   * This is the implementation of the no-op read() method of FilterReader. It
   * calls in.read() to get a buffer full of characters, then strips out the
   * HTML tags. (in is a protected field of the superclass).
   */
  @Override
  public int read(char[] buf, int from, int len) throws IOException {
      // how many characters have been read
      int charCount = 0;

      // Loop, because we might read a bunch of characters, then strip them
      // all out, leaving us with zero characters to return.
      while (charCount == 0) {
          // Read characters
          charCount = super.read(buf, from, len);
          if (charCount == -1) {
              // Check for EOF and handle it.
              return -1;
          }
          // Loop through the characters we read, stripping out HTML tags.
          // Characters not in tags are copied over previous tags
          // Index of last non-HTML char
          int last = from;
          for (int i = from; i < from + charCount; i++) {
              // If not in an HTML tag
              if (!intag) {
                  if (buf[i] == '<') {
                      // Check for tag start
                      intag = true;
                  } else {
                      // and copy the character
                      buf[last++] = buf[i];
                  }
              } else if (buf[i] == '>') {
                  // check for end of tag
                  intag = false;
              }
          }
          // Figure out how many characters remain
          charCount = last - from;
      }

      // Then return that number.
      return charCount;
  }

  /**
   * This is another no-op read() method we have to implement. We implement it
   * in terms of the method above. Our superclass implements the remaining
   * read() methods in terms of these two.
   */
  @Override
  public int read() throws IOException {
      char[] buf = new char[1];
      int result = read(buf, 0, 1);
      if (result == -1) {
          return -1;
      } else {
          return (int) buf[0];
      }
  }
}
  • RemoveHTMLReaderTest.java
package org.o7planning.tutorial.javaio.filter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

public class RemoveHTMLReaderTest {

    public static void main(String[] args) throws IOException {
        

        // Create Reader object from StringReader constructor.
        Reader in = new StringReader("<h1>Hello \n <b>World</b><h1>");

        RemoveHTMLReader filterReader = new RemoveHTMLReader(in);
        BufferedReader br = new BufferedReader(filterReader);

        String s = null;
        while ((s = br.readLine()) != null) {
            System.out.println(s);
        }
        br.close();
    }
}
Results of running the example:

10- Class java.io.FilterWriter

FilterWriter which is a stream of writing characters writes selectively requested characters. Typically, you write a subclass of FilterWriter and can override its methods, and write in stream in your manner.
The following example illustrates how data are converted when being wrote in the stream. It's can be considered a simple way of Encryption.
  • Rot13.java
package org.o7planning.tutorial.javaio.filter;

public class Rot13 {
    
    /**
     * <pre>
     *   a ==> n
     *   b ==> o
     *   c ==> p
     *   d ==> q
     *   e ==> r
     *   ...
     *   y ==> l
     *   z ==> m
     * </pre>
     */
    public static int rotate(int inChar) {
        int outChar;
        
        if (inChar >= (int) 'a' && inChar <= (int) 'z') {
            outChar = (((inChar - 'a') + 13) % 26) + 'a';
        } else if (inChar >= (int) 'A' && inChar <= (int) 'Z') {
            outChar = (((inChar - 'A') + 13) % 26) + 'A';
        } else {
            outChar = inChar;
        }
        return outChar;
    }
    
    // Test
    public static void main(String[] args)  {
        for(char ch='a'; ch<='z';ch++ ) {
            char m= (char)rotate(ch);
            System.out.println("ch="+ch+" ==> "+ m);    
        }       
        
    }
}
 
  • RotateWriter.java
package org.o7planning.tutorial.javaio.filter;

import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;

public class RotateWriter extends FilterWriter {

   // must provide constructor to extend FilterWriter;
   // objective is to allow constructing a filter stream
   // connecting to any character output stream (class Writer)
   public RotateWriter(Writer out) {
       super(out);
   }

   // override one or more write methods to perform filtering
   // (we override both to be safe)
   @Override
   public void write(int outChar) throws IOException {
       super.write(Rot13.rotate(outChar));
   }

   @Override
   public void write(char[] cbuf, int offset, int length) throws IOException {
       char[] tempbuf = new char[length];
       for (int i = 0; i < length; i++) {
           tempbuf[i] = (char) Rot13.rotate(cbuf[offset + i]);
       }
       super.write(tempbuf, 0, length);
   }

}
  • RotateWriterTest.java
package org.o7planning.tutorial.javaio.filter;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

public class RotateWriterTest {

    
    public static void main(String[] args) throws IOException  {
        String s="abcdef";
        
        Writer writer= new StringWriter();
        
        RotateWriter rw= new RotateWriter(writer);
        rw.write(s.toCharArray(),0,s.length());
        
        rw.close();
        
        String rotateString = writer.toString();
        System.out.println("rotateString="+ rotateString);
    }
}

 
Results of running the example:

11- Class java.util.PushbackReader

The PushbackReader class allows one or more characters to be returned to the input stream. This allows you to look ahead in the input stream. Here are its two constructors:
public PushbackReader(Reader inputStream)
public PushbackReader(Reader inputStream, int bufSize)
Some additional methods:
// Pushes back a single character by copying it to
// the front of the pushback buffer.
// (like - move the cursor back one position)**
public void unread(int c) throws IOException
  • PushbackReaderDemo.java
package org.o7planning.tutorial.javaio.pushback;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.PushbackReader;

class PushbackReaderDemo {

    public static void main(String args[]) throws IOException {
        String s = "if (a == 4) a = 0;\\n";
        char buf[] = new char[s.length()];
        s.getChars(0, s.length(), buf, 0);
        CharArrayReader in = new CharArrayReader(buf);
        PushbackReader f = new PushbackReader(in);
        int c;
        while ((c = f.read()) != -1) {
            switch (c) {

            // Found character '='
            case '=':

                // Read next character, (after found '-')
                if ((c = f.read()) == '=') {
                    System.out.print(".eq.");
                }
                // If next character different from '='.
                else {
                    System.out.print("<-");

                    // Pushes back a single character by copying it to
                    // the front of the pushback buffer.
                    // (like - move the cursor back one position)
                    f.unread(c);
                }
                break;
            default:
                System.out.print((char) c);
                break;
            }
        }
    }

}
Results of running the example:

12- Class java.io.PrintWriter

// Constructor
// PrintWriter is direct subclass of Writer .
// It can wrap a character output stream (Writer) or binary output stream (OutputStream), ..
public PrintWriter(Writer out) // Wrap a character stream
public PrintWriter(Writer out,boolean autoFlush)
public PrintWriter(OutputStream out) // Wrap binary stream.
public PrintWriter(OutputStream out,boolean autoFlush)
public PrintWriter(String fileName)
...

// Some methods:
public void println(String s)
public void print(char ch)
  • StackTraceToFile.java
package org.o7planning.tutorial.javaio.printwriter;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;

public class StackTraceToFile {

    public static void main(String[] args) {
        try {
           
            // Do something here
            // Exception, error divided by 0.
            int i = 10 / 0;
        } catch (Exception e) {
            System.out.println("EXCEPTION ....");
            try {
                File dir = new File("C:/test");
                // Create directories if it not exists.
                dir.mkdirs();
                // Create stream to write file.
                Writer w = new FileWriter("C:/test/stackTrace.txt");
               
                // Create PrintWriter object wrap 'w'
                // Data written to the PrintWriter will be pushed into 'w'.
                PrintWriter pw = new PrintWriter(w);
                
                // Write 'stack trace' to 'pw'.
                e.printStackTrace(pw);
                System.out.println("Finish !");
            } catch (Exception e1) {
                System.out.println("Error:" + e);
            }
        }
    }

}
  • StackTraceToString.java
package org.o7planning.tutorial.javaio.printwriter;

import java.io.PrintWriter;
import java.io.StringWriter;

public class StackTraceToString {

    public static void main(String[] args) {
        try {
           
            // Do something here
            // Exception, error divided by 0.            
            int i = 1000 / 0;
        } catch (Exception e) {
            System.out.println("EXCEPTION ....");
            try {
                StringWriter sw = new StringWriter();
               
                // Create PrintWriter object wrap 'sw'
                // Data written to the PrintWriter will be pushed into 'sw'.                
                PrintWriter pw = new PrintWriter(sw);
                
                // Write 'stack trace' to 'pw'.
                e.printStackTrace(pw);
                
                StringBuffer sb = sw.getBuffer();
                String s = sb.toString();
                System.out.println("Exception String:");
                System.out.println(s);
            } catch (Exception e1) {
                System.out.println("Error:" + e);
            }
        }

    }

}
Results of running the example:

13- Class java.io.CharArrayReader

  • CharArrayReaderDemo.java
package org.o7planning.tutorial.javaio.chararray;

import java.io.CharArrayReader;
import java.io.IOException;

public class CharArrayReaderDemo {
    
    public static void main(String args[]) throws IOException {
        
        String tmp = "abcdefghijklmnopqrstuvwxyz";
        int length = tmp.length();
        char c[] = new char[length];
        tmp.getChars(0, length, c, 0);
        
        CharArrayReader input1 = new CharArrayReader(c);
        CharArrayReader input2 = new CharArrayReader(c, 0, 5);
        
        int i;
        System.out.println("input1 is:");
        while ((i = input1.read()) != -1) {
            System.out.print((char) i);
        }
        System.out.println();
        System.out.println("input2 is:");
        while ((i = input2.read()) != -1) {
            System.out.print((char) i);
        }
        System.out.println();
    }
}
Results of running the example:

14- Class java.io.CharArrayWriter

Some additional methods:
// Writes the contents of the buffer to another character stream.
public void writeTo(Writer out) throws IOException
  • CharArrayWriterDemo.java
package org.o7planning.tutorial.javaio.chararray;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class CharArrayWriterDemo {

    public static void main(String args[]) throws IOException {
        char c[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
        CharArrayWriter out = new CharArrayWriter();
        out.write(c);

        File dir = new File("C:/test");
        dir.mkdirs();

        FileWriter f1 = new FileWriter(new File("C:/test/a.txt"));
        // File written successfully.
        out.writeTo(f1);

        FileWriter f2 = new FileWriter(new File("C:/test/b.txt"));
        // File written successfully.
        out.writeTo(f2);

        f1.close();
        f2.close();

        // CharArrayWriter is closed.
        out.close();

        FileWriter f3 = new FileWriter(new File("C:/test/c.txt"));
        // Write again to a file.
        // No Exception from CharArrayWriter but no data will be written.
        out.writeTo(f3);

        System.out.println("Done");
    }
}

15- Class java.io.PipedReader

  • TODO

16- Class java.io.PipedWriter