Java IO 教程

Java IO:BufferedReader 类

在 Java 的 I/O 操作里,java.io.BufferedReader 类发挥着关键作用,它能为 Reader 实例提供缓冲功能。而缓冲机制能够显著提升 I/O 操作的速度。与普通方式一次仅从底层 Reader 读取一个字符不同,BufferedReader 会一次读取一大块数据(以数组形式)。这种读取方式效率更高,特别是在涉及磁盘访问或者处理大量数据时,优势尤为明显。

BufferedReader 和 BufferedInputStream 有相似之处,但也存在本质区别。二者虽都具备缓冲特性,不过 BufferedReader 专注于读取字符(也就是文本数据),而 BufferedInputStream 则用于读取原始字节数据。

由于 BufferedReader 类是 Reader 类的子类,所以在任何需要使用 Reader 的场景中,你都可以用 BufferedReader 来替代,从而借助其缓冲功能优化性能。

BufferedReader 示例

如果要为 Reader 实例添加缓冲功能,只需将其包装在 BufferedReader 中。如下所示:

package com.hxstrive.java_io;

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

public class BufferedReaderExample {
    public static void main(String[] args) throws IOException {
        // 创建了一个 BufferedReader,缓冲区大小为 8192
        try(Reader reader = new BufferedReader(new FileReader("data.txt"), 1024*8)) {
            // 一次读取一个字符,实际是从 BufferedReader 内部的字符数组中获取的
            int val = reader.read();
            while(val != -1) {
                System.out.print((char)val);
                val = reader.read();
            }
        }
    }
}

上述示例,创建了一个封装 FileReader 的 BufferedReader。BufferedReader 将从 FileReader 中读取一个字符块(通常是一个字符数组)。因此,从 read() 返回的每个字符都会从这个内部数组中返回。当数组完全读取完毕后,BufferedReader 会向数组中读取新的数据块。

BufferedReader 缓冲区大小

你可以通过 BufferedReader 构造函数的参数去设置其内部缓冲区大小。如下:

Reader reader = new BufferedReader(new FileReader("data.txt"), 1024*8)

上述示例,将 BufferedReader 内部缓冲区大小设置为 8 KB。最好使用 1024 字节倍数的缓冲区大小。因为,这与硬盘等大多数内置缓冲区的效果最佳。

除了为 Reader 实例添加缓冲区外,BufferedReader 的行为与 Reader 非常相似。不过,BufferedReader 有一个额外的方法,即 readLine() 方法。如果需要一行一行地读取输入内容,这个方法会非常方便。下面是 BufferedReader 的 readLine() 方法示例:

package com.hxstrive.java_io;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderExample2 {
    public static void main(String[] args) throws IOException {
        // 注意,readLine() 是 BufferedReader 中的方法,因此
        // Reader 不能使用,必须将类型设置为 BufferedReader,如下:
        try(BufferedReader reader = new BufferedReader(new FileReader("data.txt"), 1024*8)) {
            String line = reader.readLine(); // 一次读取一行
            while(null != line) {
                System.out.println(line);
                line = reader.readLine(); // 一次读取一行
            }
        }
    }
}

注意,上述示例中,readLine() 方法将返回从 BufferedReader 读取的文本行(在找到换行符之前的所有文本)。如果没有更多数据可从底层 Reader 读取,那么 BufferedReader 的 readLine() 方法将返回 null。

从 BufferedReader 读取字符

BufferedReader 的 read() 方法返回一个 int,其中包含下一个读取字符的字符值。如果 read() 方法返回 -1,则表示 BufferedReader 中已无数据可读,可以关闭了。

下面是一个从 BufferedReader 中读取所有字符的示例:

try(Reader reader = new BufferedReader(new FileReader("data.txt"), 1024*8)) {
    int val = reader.read(); // 读取一个字符
    while(val != -1) {
        System.out.print((char)val);
        val = reader.read();
    }
}

注意,该示例代码首先从 BufferedReader 中读取一个字符,然后检查字符数值是否等于 -1。如果不等于,则处理该字符并继续读取,直到 BufferedReader 的 read() 方法返回 -1。

如前所述,BufferedReader 实际上是从底层 Reader 读取字符数组并逐个返回这些字符,而不是将每次 read() 调用都转发给底层 Reader。当读取完内部缓冲区中的所有字符后,BufferedReader 会尝试再次填充缓冲区,直到无法从底层 Reader 读取更多字符为止。

从 BufferedReader 读取字符数

BufferedReader 类也有一个用来读取字符数组的 read(char[], offset, length) 方法,该方法将字符数组(char[])、起始偏移量(offset)和长度(length)作为参数。字符数组是 read() 方法读取字符存放的位置。偏移参数是 read() 方法读取的数据应从字符数组什么位置开始存放。长度参数是 read() 方法从偏移量开始向前读入的字符数。

下面是使用 BufferedReader 将字符数组读入字符数组的示例:

package com.hxstrive.java_io;

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

public class BufferedReaderExample3 {
    public static void main(String[] args) throws IOException {
        try(Reader reader = new BufferedReader(new FileReader("data.txt"), 1024*8)) {
            // 字符数组
            char[] chars = new char[32];
            // 从 Reader 中读取字符数组
            int len = reader.read(chars, 0, chars.length);
            while(len != -1) {
                // 输出到控制台
                System.out.print(new String(chars, 0, len));
                len = reader.read(chars, 0, chars.length);
            }
        }
    }
}

注意,BufferedReader 的 read(char[], offset, length) 方法返回读入字符数组的字符数,如果 BufferedReader 中没有更多字符可读取,则返回 -1,例如,如果 BufferedReader 所连接的文件已到末尾。

从 BufferedReader 取一行

BufferedReader 有一个名为 readLine() 的特殊读取方法,可从 BufferedReader 的内部缓冲区读取一整行文本。readLine() 方法返回一个字符串。如果 BufferedReader 中没有其他行可读,则 readLine() 方法返回 null。下面是一个使用 BufferedReader 逐行读取文本文件的示例:

BufferedReader bufferedReader =
    new BufferedReader(new FileReader("data.txt"));
String line = bufferedReader.readLine(); // 读取一行
while(line != null) {
    System.out.println(line);
    line = bufferedReader.readLine(); // 读取一行
}

注意,一次读取一个字符数组要比从 Reader 中一次读取一个字符快。不过,由于 BufferedReader 已经进行了一些内部缓冲,因此两者之间的差异可能不会像不使用缓冲的 Reader 那样显著。

跳过字符

BufferedReader 类有一个名为 skip() 的方法,可用于跳过输入中不想读取的字符数。你可以将要跳过的字符数作为参数传递给 skip() 方法。下面是一个跳过 BufferedReader 中的字符的示例:

long charsSkipped = bufferedReader.skip(24);

上述示例,告诉 BufferedReader 跳过 BufferedReader 中的下一个 24 个字符。skip() 方法返回跳过的实际字符数。大多数情况下,跳过的字符数与您请求跳过的字符数相同,但如果 BufferedReader 中剩余的字符数少于您请求跳过的字符数,则返回的跳过字符数可能少于你请求跳过的字符数。

关闭 BufferedReader

当你完成从 BufferedReader 中读取字符后,一定要记得关闭它。关闭 BufferedReader 也会关闭底层的 Reader 实例。

关闭 BufferedReader 仅需要调用其 close() 方法。下面是关闭 BufferedReader 的示例:

bufferedReader.close();

当然,你还可以使用 Java7 中引入的 try-with-resources 结构,让它自动帮你关闭 BufferedReader。如下:

Reader reader = new FileReader("data.txt");
try(BufferedReader bufferedReader = new BufferedReader(reader)){
    String line = bufferedReader.readLine();
    while(line != null) {
        System.out.print(line);
        line = bufferedReader.readLine();
    }
}

注意,第一个 FileReader 实例并没有在 try-with-resources 代码块中创建。这意味着 try-with-resources 块不会自动关闭 FileReader 实例。不过,当 BufferedReader 关闭时,它也会关闭它底层的 Reader 实例,因此 FileReader 实例会在 BufferedReader 关闭时被关闭。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号