必须用inputstreamreader包装inputstream,因为文件底层是字节流,而文本需字符解码;inputstreamreader负责按指定编码(如utf-8)将字节转为unicode字符,避免依赖平台默认编码导致乱码;再套bufferedreader提供缓冲和readline()能力,提升读取效率并防止i/o频繁调用。

要实现高效且编码准确的文本读取,关键不是单独用 InputStreamReader 或 BufferedReader,而是把它们按职责链组合:InputStreamReader 负责字节→字符的解码(指定编码),BufferedReader 负责缓冲和按行高效读取。两者缺一不可,顺序也不能颠倒。
为什么必须用 InputStreamReader 包装 InputStream
文件底层是字节流(FileInputStream),而文本是字符概念。直接用 FileReader 会依赖系统默认编码(如 Windows 的 GBK),极易乱码。InputStreamReader 就是专为解决这个问题设计的“解码桥”:
- 它接收字节流,并按你指定的编码(如 UTF-8、GBK、ISO-8859-1)逐字节还原成 Java 内部的 Unicode 字符
- 不指定 charsetName 时,它会用平台默认编码,这是大多数乱码的根源
- 常见写法:
new InputStreamReader(new FileInputStream("a.txt"), StandardCharsets.UTF_8)
为什么还要套一层 BufferedReader
InputStreamReader 解码正确了,但每次调用 read() 或 readLine() 都可能触发一次底层 I/O,对大文件很慢。BufferedReader 的作用就是加一层内存缓冲区(默认 8192 字节):
- 一次性从底层读多字节进缓冲区,后续读取优先从内存拿,大幅减少系统调用次数
- 提供真正实用的
readLine()方法——FileReader 本身没有这个能力 - 缓冲区大小可自定义,但默认值对绝大多数场景已足够
标准安全写法(推荐直接复用)
结合 try-with-resources 自动关闭资源,避免泄漏:
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"),
StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
- 用
StandardCharsets.UTF_8替代字符串"UTF-8",类型安全且无需处理 UnsupportedEncodingException - 如果文件是 GBK 编码(如旧版 Windows 记事本保存),把
UTF_8换成GBK即可 - 不要混用
readLine()和read(),BufferedReader 内部缓冲机制可能导致跳过或重复读取
常见编码问题排查要点
读出来仍是乱码?先检查这三点:
- 确认源文件真实编码(可用编辑器如 VS Code 底部状态栏查看,或用命令行
file -i filename) - 确保 InputStreamReader 构造时传入的 charsetName 与文件实际编码完全一致
- 避免在读取后又用错误编码二次转换,比如
new String(str.getBytes("GBK"), "UTF-8")这类操作通常多余且危险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











