FileReader读中文乱码因默认用系统编码(如GBK)而非UTF-8,应改用InputStreamReader(new FileInputStream("a.txt"), "UTF-8");FileWriter同理需换OutputStreamWriter指定UTF-8;Files工具类虽简洁但易OOM,大文件须流式处理;BufferedReader.readLine()返回null表示读完,正确写法为while((line=br.readLine())!=null)。

FileReader 读取文本时中文乱码怎么解决
默认用 FileReader 读文件,遇到中文基本就乱码——它底层固定按平台默认编码(如 Windows 的 GBK)解码,但现代文本文件几乎全是 UTF-8。必须绕过 FileReader,改用 InputStreamReader 配合 FileInputStream 显式指定 "UTF-8"。
常见错误现象:FileReader 读出的字符串含 或其他异常符号,file.encoding 系统属性也无效。
- 不要写
new FileReader("a.txt"),这是陷阱 - 改用
new InputStreamReader(new FileInputStream("a.txt"), "UTF-8") - 务必在
try-with-resources中打开,避免流未关闭导致文件句柄泄漏 - 如果文件路径含空格或中文,确保传入的是
File对象或已 URL-decode 的路径,而非裸字符串
FileWriter 写入中文不生效或被截断
FileWriter 同样默认用系统编码写入,且默认是覆盖模式(append = false),稍不注意就丢数据。更关键的是:它不支持直接指定编码,所以写 UTF-8 中文必须换用 OutputStreamWriter。
使用场景:日志追加、配置生成、导出简短文本结果。
- 写入前确认目标目录存在,
FileWriter不会自动创建父目录,报FileNotFoundException很可能是因为路径中的文件夹不存在 - 需要追加内容?用
new FileWriter("b.txt", true),第二个参数为true;但依然要换OutputStreamWriter才能控编码 - 正确写法示例:
new OutputStreamWriter(new FileOutputStream("b.txt", true), "UTF-8") - 写完记得
flush(),尤其在非自动关闭场景下,否则缓冲区内容可能滞留不落盘
为什么不用 Files.readAllLines / Files.write 代替?
Java 7+ 的 Files 工具类确实更简洁,但有隐含限制:它把整个文件一次性加载进内存。对几 MB 以内的小文本没问题,但处理几十 MB 的日志或 CSV 就容易 OOM。
性能与兼容性影响:
-
Files.readAllLines(path, UTF_8)返回List,适合后续随机访问行;FileReader+BufferedReader流式读取适合逐行处理、内存可控 -
Files.write()是原子写入(先写临时文件再重命名),适合要求强一致性的场景;而FileWriter是直写,崩溃时可能留下半截文件 - Android API 26 以下不支持
Files的部分方法,老环境必须回退到流式方案
BufferedReader.readLine() 返回 null 却没报错,是文件空还是读完了?
readLine() 在流末尾返回 null,这是正常行为,不是错误。新手常误判为异常,进而跳过循环或提前退出。
容易踩的坑:
- 别写
while (br.readLine() != null) { ... }—— 这会丢掉第一行,因为readLine()被调用了两次(条件判断一次,循环体里又一次) - 正确写法:
String line; while ((line = br.readLine()) != null) { ... } - 如果文件为空,第一次
readLine()就返回null,循环体不会执行,这符合预期 - 注意:Windows 换行符
\r\n、Unix 的\n、Mac 的\r都会被readLine()自动剥离,无需额外 trim
实际项目里最常被忽略的是流的生命周期管理——哪怕用了 try-with-resources,如果在 catch 块里吞掉异常又没 re-throw,问题会静默失败。还有就是硬编码路径,测试时用相对路径跑通,上线后因工作目录变化直接找不到文件。










