Table Of Content
- Introduction
- The difference between binary streams and character streams
- Overview of the character streams
- Class java.io.Reader
- Class java.io.Writer
- How a binary stream is converted into a character stream?
- Class java.io.BufferedReader
- Class java.io.BufferedWriter
- Class java.io.FilterReader
- Class java.io.FilterWriter
- Class java.util.PushbackReader
- Class java.io.PrintWriter
- Class java.io.CharArrayReader
- Class java.io.CharArrayWriter
- Class java.io.PipedReader
- Class java.io.PipedWriter
Java IO Character Streams Tutorial
View more Tutorials:
- Introduction
- The difference between binary streams and character streams
- Overview of the character streams
- Class java.io.Reader
- Class java.io.Writer
- How a binary stream is converted into a character stream?
- Class java.io.BufferedReader
- Class java.io.BufferedWriter
- Class java.io.FilterReader
- Class java.io.FilterWriter
- Class java.util.PushbackReader
- Class java.io.PrintWriter
- Class java.io.CharArrayReader
- Class java.io.CharArrayWriter
- Class java.io.PipedReader
- Class java.io.PipedWriter
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:
Binary Stream, each one reads/writes a byte (equivalent to 8 bits)

Meanwhile, character stream read/write a character. It is dependent on type of encoding (UTF-8, UTF-16,...) to decide the number of bytes in each reading/writing 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 String with UTF-16 encoding.
- The next characters are encoded by 2 bytes.
- For example, 'J' character is encoded by 2 bytes (0 and 74)
- 'P' character is encoded by 2 bytes (0 and 80)
- .....
- When reading from file with UTF-16 encoding, it will ignore two first bytes and read two consecutive bytes into one character.

UTF-8:
It will be different if the same above-mentioned Japanese text is encoded by UTF-8. You can see that bytes are stored in hard disk drive:
- With ASCII characters, it will only use one byte for storage.
- 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, base on a UTF-8 Table.
- Read the first byte, if it <= 127, then it's an ASCII character.
- Else if it >127, then it needs to read the second byte, and check whether two bytes can be combined into one character based on the UTF-8 Table.
- If the first 2 bytes do not correspond to 1 character, It will read the next byte and combine them into a character.
- UTF-8 uses up to 3 bytes to store 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.
Hierarchies of Classes:

Reader is a abstract class. it is the base class for character reading streams.
Create a file test_reader.txt to start an example with Reader:

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 { // Create a Reader (Character stream), to read a file. // With default encoding. Reader r = new FileReader("test_reader.txt"); int i = -1; // Read each character in turn. while ((i = r.read()) != -1) { // Cast to char. System.out.println((char) i); } r.close(); } }
Run the example:

The next example is to read many characters in a reading turn. 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 { // Create a Reader, to read a file. // With default encoding. Reader r = new FileReader("test_reader.txt"); // Create temporary array of characters. char[] temp = new char[10]; int i = -1; // read(char[]) method: // Read multiple characters at once, // and assign them to the elements of the array. // Return the number of characters read. // Or returns -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(); } }
Writer is an abstract class, it is the base class for the character writing stream.
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 'C:/test' directory, if it does not exists. dir.mkdirs(); // Create a Writer, to write data to the file. // Using default encoding. 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]; int j = (int) ch; // 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 an array of characters into 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 'C:/test' directory, if it does not exists. dir.mkdirs(); // Create a Writer, to write the data to the 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 all characters to the stream. w.write(chars); // Java often uses buffers to store data temporarily. // When the buffer is full, it flush the data to the file. // You can proactively flush data into the file. w.flush(); // Write 'new line' character to stream. 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. // Also finish writing data. w.close(); } }
Results of running the example:

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 wrap binary stream above. // 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 'C:test' directory if it does not exists. dir.mkdirs(); // Create a OutputStream, to write data to a file. OutputStream out = new FileOutputStream("C:/test/test_write_utf8.txt"); // Create character stream wrap a OutputStream above. // encoding UTF-8. Writer writer = new OutputStreamWriter(out, "UTF-8"); String s = "JP日本-八洲"; writer.write(s); writer.close(); } }
Results of running the example

If you want to read each line of data of a text file. BufferedReader is a good choice.

// BufferedReader is a direct subclass of Reader. // Constructor: public BufferedReader(Reader in); // A convenient method of BufferedReader. // Read a line of text. public String readLine();
Ví dụ:
// Example 1: Reader r=new FileReader("C:/test.txt"); BufferedReader br=new BufferedReader(r); // Example 2: InputStream in = new FileInputStream("C:/test.txt"); Reader r = new InputStreamReader(in, "UTF-8"); BufferReader br = new BufferedReader(r);

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:

BufferedWriter is a direct subclass of the Writer class.
// Create a BufferedWriter object // by wrapping another Writer object. public BufferedWriter(Writer out); // Equivalent to write ('\ n'); public String newLine();
Ví dụ:
// Create Writer object. Writer w=new FileWriter("C:/test/test_bufferedWriter.txt"); // Create BufferedWriter object wrap a writer. BufferedWriter bw=new BufferedWriter(w); bw.write("Hello.."); // Write a new line character. bw.newLine();
FilterReader is a subclass of Reader. It reads selectively characters on demand. For example, you want to read an HTML document, and ignore the characters in the tags. You need to write a subclass of FilterReader and then use that subclass. You cannot directly use FilterReader since it is a abstract class.

Example, create a subclass of FilterReader, to read HTML document but ignore characters in tag.
Input: "<h1>Hello</h1>" ==> output: "Hello".
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 { boolean intag = false; public RemoveHTMLReader(Reader in) { super(in); } // Override this method. // The principle would be: // Only read characters outside the tags. @Override public int read(char[] buf, int from, int len) throws IOException { int charCount = 0; while (charCount == 0) { charCount = super.read(buf, from, len); if (charCount == -1) { // End the stream. return -1; } int last = from; for (int i = from; i < from + charCount; i++) { // If not in an HTML tag. if (!intag) { if (buf[i] == '<') { intag = true; } else { buf[last++] = buf[i]; } } else if (buf[i] == '>') { intag = false; } } charCount = last - from; } return charCount; } // Override this method too. @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:

FilterWriter is a direct subclass of Writer. It writes selectively characters on demand. You need to write a subclass of FilterWriter and override some methods of the FilterWriter class.

Example: Characters changed when writing to stream.
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 { public RotateWriter(Writer out) { super(out); } // override one or more write methods to perform filtering. // (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:

The PushbackReader class allows one or more characters to be returned to the stream after reading them. Here are its two constructors:

public PushbackReader(Reader inputStream) public PushbackReader(Reader inputStream, int bufSize)
Some additional methods:
// Pushes back a single character to stream. public void unread(int c) throws IOException
Example:
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."); } 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:


// Constructor: // Wrap a Writer object. public PrintWriter(Writer out) public PrintWriter(Writer out,boolean autoFlush) // Wrap a OutputStream object. public PrintWriter(OutputStream out) public PrintWriter(OutputStream out,boolean autoFlush) public PrintWriter(String fileName) ... // Method: 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. // 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 data to the file. Writer w = new FileWriter("C:/test/stackTrace.txt"); // Create PrintWriter object wrap Writer '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 // Error divided by 0. int i = 1000 / 0; } catch (Exception e) { System.out.println("EXCEPTION ...."); try { StringWriter sw = new StringWriter(); // Create PrintWriter object wrap StringWriter '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:


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:


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")); // Write data of 'out' to 'f1'. out.writeTo(f1); FileWriter f2 = new FileWriter(new File("C:/test/b.txt")); // Write data of 'out' to 'f2'. out.writeTo(f2); f1.close(); f2.close(); // Close CharArrayWriter 'out'. out.close(); FileWriter f3 = new FileWriter(new File("C:/test/c.txt")); // With CharArrayWriter, after close. // writeTo(..) method no longer works. // Also does not cause an exception if you use writeTo (..). out.writeTo(f3); System.out.println("Done!"); } }

- TODO
PipeReaderExample1.java
package org.o7planning.tutorial.javaio.pipereader; import java.io.IOException; import java.io.Reader; import java.io.PipedReader; import java.io.PipedWriter; public class PipeReaderExample1 { private Reader pipedReader; public static void main(String[] args) throws IOException, InterruptedException { new PipeReaderExample1().test(); } private void test() throws IOException, InterruptedException { // Create a 'pipedWriter', PipedWriter pipedWriter = new PipedWriter(); // Data writing to 'pipedWriter' // will automatically appear in 'pipedReader'. pipedReader = new PipedReader(pipedWriter); new ThreadRead().start(); char[] chs = new char[] { 'a', 'a', 'b', 'c' , 'e' }; // Write data to 'pipedWriter'. for (char ch : chs) { pipedWriter.write(ch); Thread.sleep(1000); } pipedWriter.close(); } // A Thread to read the data that appears on 'pipedReader'. class ThreadRead extends Thread { @Override public void run() { try { int data = 0; while ((data = pipedReader.read()) != -1) { System.out.println((char) data); } } catch (Exception e) { e.printStackTrace(); } finally { closeQuietly(pipedReader); } } } private void closeQuietly(Reader is) { if (is != null) { try { is.close(); } catch (IOException e) { } } } }

PipeReaderExample2.java
package org.o7planning.tutorial.javaio.pipereader; import java.io.BufferedReader; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; import java.io.Reader; public class PipeReaderExample2 { private BufferedReader bufferedReader; public static void main(String[] args) throws IOException, InterruptedException { new PipeReaderExample2().test(); } private void test() throws IOException, InterruptedException { // Create a 'pipedWriter', PipedWriter pipedWriter = new PipedWriter(); // Data writing to 'pipedWriter' // will automatically appear in 'pipedReader'. PipedReader pipedReader = new PipedReader(pipedWriter); // Tạo một 'bufferedReader' wrapped 'pipedReader'. bufferedReader = new BufferedReader(pipedReader); new ThreadRead().start(); String[] strs = new String[] { "Hello ", "There", "\n", "I am ", "Tran" }; // Write data to 'pipedWriter'. for (String str : strs) { pipedWriter.write(str); Thread.sleep(500); } pipedWriter.close(); } // A Thread to read the data that appears on 'bufferedReader' ('pipedReader'). class ThreadRead extends Thread { @Override public void run() { try { String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } finally { closeQuietly(bufferedReader); } } } private void closeQuietly(Reader reader) { if (reader != null) { try { reader.close(); } catch (IOException e) { } } } }

