InputStreamReader and OutputStreamWriter
InputStreamReader and OutputStreamWriter are the bridges between byte streams and character streams. An InputStreamReader reads bytes from an InputStream and decodes them into characters. An OutputStreamWriter takes characters and encodes them into bytes for an OutputStream. The rule for turning characters into bytes is called a character set (or charset), such as UTF-8. In this lesson, you will learn how these bridges work and why choosing the right charset matters.
Characters and Bytes
A computer stores only bytes. A charset is a table that says which bytes stand for which characters:
| Character | UTF-8 bytes | Count |
|---|---|---|
A | 41 | 1 byte |
é | c3 a9 | 2 bytes |
₹ | e2 82 b9 | 3 bytes |
న (Telugu "na") | e0 b0 a8 | 3 bytes |
UTF-8 can represent every character in every language and is the standard for files and the web. Older charsets such as ISO-8859-1 only cover a few hundred characters.
Constructors
| Constructor | Description |
|---|---|
new InputStreamReader(InputStream in, Charset cs) | It is used to decode bytes from in into characters. |
new OutputStreamWriter(OutputStream out, Charset cs) | It encodes characters into bytes for out. |
FileReader is simply an InputStreamReader over a FileInputStream, and FileWriter is an OutputStreamWriter over a FileOutputStream.
Example: Encoding and Decoding
Let's see an example that writes text containing non-English characters and reads it back.
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class Charsets {
public static void main(String[] args) throws IOException {
String text = "Navika ate crème brûlée at a café";
try (Writer w = new OutputStreamWriter(
new FileOutputStream("note.txt"), StandardCharsets.UTF_8)) {
w.write(text);
}
System.out.println(text.length() + " characters, "
+ Files.size(Path.of("note.txt")) + " bytes");
try (BufferedReader right = new BufferedReader(new InputStreamReader(
new FileInputStream("note.txt"), StandardCharsets.UTF_8))) {
System.out.println("UTF-8: " + right.readLine());
}
try (BufferedReader wrong = new BufferedReader(new InputStreamReader(
new FileInputStream("note.txt"),
StandardCharsets.ISO_8859_1))) {
System.out.println("ISO-8859-1: " + wrong.readLine());
}
}
}33 characters, 37 bytes
UTF-8: Navika ate crème brûlée at a café
ISO-8859-1: Navika ate crème brûlée at a caféExplanation: The text has 33 characters but 37 bytes, because each of the four accented letters takes 2 bytes in UTF-8. Reading with the same charset gives the text back exactly. Reading with the wrong charset turns each byte into a separate wrong character; this garbled text is called mojibake.
Example: Reading the Keyboard
System.in is a byte stream, so to read lines of text from the keyboard you bridge it:
BufferedReader keyboard = new BufferedReader(
new InputStreamReader(System.in, StandardCharsets.UTF_8));
String name = keyboard.readLine();This is exactly what the Scanner class and Console do for you behind the scenes.
Common Mistakes
Creating new String(bytes) without a charset. Use new String(bytes, StandardCharsets.UTF_8).
Counting characters with Files.size. It returns bytes, which differ from characters in UTF-8.
Using a Reader to copy binary files. The decoding step corrupts the data.
Practice
- Easy: Write your name in English and in Telugu to a file with UTF-8, then print the number of characters and bytes.
- Harder: Write a method that converts a text file from ISO-8859-1 to UTF-8 using
InputStreamReaderandOutputStreamWriter.
Key Takeaways
Was this tutorial helpful?