Appearance
Reader
Reader
is another input stream interface provided by Java's IO library. The difference from InputStream
is that InputStream
is a byte stream, reading in bytes, while Reader
is a character stream, reading in characters:
InputStream | Reader |
---|---|
Byte stream, reads by byte | Character stream, reads by char |
Reads byte (-1, 0~255): int read() | Reads character (-1, 0~65535): int read() |
Reads byte array: int read(byte[] b) | Reads character array: int read(char[] c) |
java.io.Reader
is the superclass of all character input streams, with its main method defined as:
java
public int read() throws IOException;
This method reads the next character from the character stream and returns it as an int
, ranging from 0 to 65535. If the end of the stream is reached, it returns -1.
FileReader
FileReader
is a subclass of Reader
that can open a file and provide a Reader
. The following code demonstrates how to read all characters from a FileReader
:
java
public void readFile() throws IOException {
// Create a FileReader object:
Reader reader = new FileReader("src/readme.txt"); // What is the character encoding?
for (;;) {
int n = reader.read(); // Repeatedly call read() until -1 is returned
if (n == -1) {
break;
}
System.out.println((char)n); // Print char
}
reader.close(); // Close the stream
}
This code works fine for a plain ASCII-encoded text file. However, if the file contains Chinese characters, it may result in garbled output, as FileReader
uses the system's default encoding, which might be GBK on Windows, leading to issues with UTF-8 encoded files.
To avoid encoding issues, we need to specify the encoding when creating the FileReader
:
java
Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8);
Similar to InputStream
, Reader
is a resource that needs to be properly closed even when errors occur. Therefore, we use try (resource)
to ensure the Reader
is correctly closed whether or not an IO error occurs:
java
try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
// TODO
}
Reader
also provides a method to read multiple characters at once into a char[]
array:
java
public int read(char[] c) throws IOException;
This method returns the actual number of characters read, up to the length of the char[]
array. A return value of -1 indicates the end of the stream.
Using this method, we can set up a buffer and fill it as much as possible each time:
java
public void readFile() throws IOException {
try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
char[] buffer = new char[1000];
int n;
while ((n = reader.read(buffer)) != -1) {
System.out.println("read " + n + " chars.");
}
}
}
CharArrayReader
CharArrayReader
simulates a Reader
in memory by turning a char[]
array into a Reader
, similar to ByteArrayInputStream
:
java
try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
}
StringReader
StringReader
allows a String
to be used as a data source, functioning nearly the same as CharArrayReader
:
java
try (Reader reader = new StringReader("Hello")) {
}
InputStreamReader
How is Reader
related to InputStream
?
Aside from the special CharArrayReader
and StringReader
, a standard Reader
is constructed based on InputStream
, as it needs to read byte streams and convert them to characters based on the specified encoding. If we examine the source code of FileReader
, we see that it internally holds a FileInputStream
.
Since Reader
is essentially a byte-to-char converter based on InputStream
, it is entirely feasible to convert an existing InputStream
into a Reader
. InputStreamReader
serves as such a converter, allowing any InputStream
to be converted into a Reader
. Here’s an example:
java
// Hold an InputStream:
InputStream input = new FileInputStream("src/readme.txt");
// Convert to Reader:
Reader reader = new InputStreamReader(input, "UTF-8");
When constructing InputStreamReader
, we need to pass an InputStream
and specify the encoding, resulting in a Reader
object. The above code can be simplified using try (resource)
as follows:
java
try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
// TODO:
}
This code essentially represents one implementation of FileReader
.
When using the try (resource)
structure, closing the Reader
automatically invokes the close()
method of the underlying InputStream
, so only the outer Reader
object needs to be closed.
Tip
Using InputStreamReader
, you can convert an InputStream
into a Reader
.
Summary
Reader
defines the superclass for all character input streams.FileReader
implements file character stream input and requires encoding to be specified.CharArrayReader
andStringReader
can simulate character stream input in memory.Reader
is constructed based onInputStream
: you can convert anyInputStream
toReader
usingInputStreamReader
with specified encoding.- Always use
try (resource)
to ensureReader
is closed correctly.