当前位置: 首页 > news >正文

Java IO 流详解:字符流(Reader/Writer)与字符编码那些事

在 Java IO 体系中,流按处理数据类型可分为字节流字符流。字节流以字节为单位处理数据(如图片、视频等二进制文件),而字符流以字符为单位处理数据(如文本文件),其核心优势是能直接处理 Unicode 字符,并解决了字节流处理文本时的编码转换问题。

本文将深入解析字符流的核心组件:Reader/Writer 抽象类、FileReader/FileWriter 实现类,以及开发中绕不开的字符编码(UTF-8/GBK)问题,配合实例代码和图解,让你彻底搞懂字符流的工作原理。

一、字符流的核心:Reader 与 Writer 抽象类

字符流的顶层是两个抽象类:java.io.Reader(输入字符流)和 java.io.Writer(输出字符流)。它们定义了字符流的基本操作规范,所有字符流实现类都直接或间接继承这两个类。

1.1 Reader 抽象类:字符输入的 "模板"

Reader 是所有字符输入流的父类,其核心方法用于从数据源读取字符:

方法签名功能说明
int read()读取单个字符,返回字符的 Unicode 编码(0-65535),若已到达流末尾则返回 -1
int read(char[] cbuf)读取字符到数组 cbuf 中,返回实际读取的字符数,末尾返回 -1
int read(char[] cbuf, int off, int len)读取最多 len 个字符到数组 cbuf 中,从索引 off 开始存储
void close()关闭流,释放资源(必须调用,否则可能导致资源泄露)

注意:read() 方法是阻塞式的,若没有数据可读会一直等待。

1.2 Writer 抽象类:字符输出的 "模板"

Writer 是所有字符输出流的父类,核心方法用于向目标写入字符:

方法签名功能说明
void write(int c)写入单个字符(取 c 的低 16 位,因为 Unicode 字符占 2 字节)
void write(char[] cbuf)写入字符数组 cbuf
void write(char[] cbuf, int off, int len)写入数组 cbuf 中从 off 开始的 len 个字符
void write(String str)写入字符串 str
void write(String str, int off, int len)写入字符串 str 中从 off 开始的 len 个字符
void flush()刷新缓冲区,将数据立即写入目标(字符流默认有缓冲区,需手动刷新或关闭时自动刷新)
void close()关闭流,关闭前会自动调用 flush()

1.3 字符流与字节流的本质区别

字符流的底层依然是字节流,但它多了一层编码 / 解码处理:

  • 读取时:字节流读取字节 → 字符流通过编码表将字节转换为字符(解码)
  • 写入时:字符流将字符通过编码表转换为字节 → 字节流写入字节(编码)

用一张图直观展示两者的关系:

二、文件字符流:FileReader 与 FileWriter

FileReader 和 FileWriter 是 Reader/Writer 的直接实现类,专门用于读写文本文件(如 .txt.java 等)。

2.1 FileReader:读取文件中的字符

FileReader 继承自 InputStreamReader,它通过系统默认编码将文件字节转换为字符。构造方法如下:

// 根据文件路径创建FileReader
FileReader(String fileName) throws FileNotFoundException// 根据File对象创建FileReader
FileReader(File file) throws FileNotFoundException

使用示例:读取文本文件内容

import java.io.FileReader;
import java.io.IOException;public class FileReaderDemo {public static void main(String[] args) {// try-with-resources语法:自动关闭流,无需手动调用close()try (FileReader reader = new FileReader("test.txt")) {char[] buffer = new char[1024]; // 字符缓冲区,提高效率int len;// 循环读取,len为实际读取的字符数while ((len = reader.read(buffer)) != -1) {// 将字符数组转换为字符串输出System.out.print(new String(buffer, 0, len));}} catch (IOException e) {e.printStackTrace();}}
}

2.2 FileWriter:向文件写入字符

FileWriter 继承自 OutputStreamWriter,通过系统默认编码将字符转换为字节写入文件。构造方法如下:

// 根据文件路径创建FileWriter(覆盖写入)
FileWriter(String fileName) throws IOException// 根据File对象创建FileWriter(覆盖写入)
FileWriter(File file) throws IOException// 追加写入(第二个参数为true)
FileWriter(String fileName, boolean append) throws IOException
FileWriter(File file, boolean append) throws IOException

使用示例:向文件写入内容

import java.io.FileWriter;
import java.io.IOException;public class FileWriterDemo {public static void main(String[] args) {try (FileWriter writer = new FileWriter("output.txt", true)) { // 追加模式String content = "Hello, 字符流!\n";writer.write(content); // 写入字符串writer.flush(); // 手动刷新缓冲区(close()会自动刷新)System.out.println("写入成功");} catch (IOException e) {e.printStackTrace();}}
}

2.3 注意事项

  1. 默认编码问题FileReader/FileWriter 使用系统默认编码(可通过 System.getProperty("file.encoding") 获取),跨平台时可能导致乱码(如 Windows 默认 GBK,Linux 默认 UTF-8)。
  2. 缓冲区刷新Writer 有缓冲区,若未调用 flush() 或 close(),数据可能留在缓冲区未写入文件。
  3. 资源释放:务必关闭流,推荐使用 try-with-resources 语法(Java 7+),自动释放资源。

三、字符编码:UTF-8/GBK 与乱码根源

字符编码是字符流的核心知识点,也是乱码问题的根源。理解编码规则,才能避免在文件读写时出现乱码。

3.1 什么是字符编码?

字符编码是一套规则,定义了字符(如 'A'、' 中 ')与字节(0-255)的映射关系。计算机只能存储字节,因此需要通过编码将字符转换为字节(写入时),或通过解码将字节转换为字符(读取时)。

常见编码方式:

  • ASCII:美国标准码,仅包含英文字母、数字和符号,1 字节表示(0-127)。
  • GBK:国家标准扩展码,支持中文字符,中文占 2 字节,英文占 1 字节(兼容 ASCII)。
  • UTF-8:Unicode 的可变长度编码,英文占 1 字节,中文占 3 字节,支持全球所有字符。

3.2 乱码的产生原因

乱码的本质是编码与解码使用的规则不一致。例如:

  • 用 UTF-8 编码写入的 "中" 字(占 3 字节),若用 GBK 解码(按 2 字节解析),会得到错误字符。
  • 用 GBK 编码写入的 "中" 字(占 2 字节),若用 UTF-8 解码(按 3 字节解析),会因字节不足导致乱码。

用示意图展示乱码过程:

3.3 如何指定编码读写文件?

FileReader/FileWriter 的缺陷是无法指定编码(只能用系统默认),解决办法是使用 InputStreamReader 和 OutputStreamWriter(字符流与字节流的桥梁),它们可以指定编码。

示例:用 UTF-8 编码读写文件

import java.io.*;public class EncodingDemo {public static void main(String[] args) {// 写入:指定UTF-8编码try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("utf8.txt"), "UTF-8")) {writer.write("你好,UTF-8!");} catch (IOException e) {e.printStackTrace();}// 读取:指定UTF-8编码try (InputStreamReader reader = new InputStreamReader(new FileInputStream("utf8.txt"), "UTF-8")) {char[] buffer = new char[1024];int len;while ((len = reader.read(buffer)) != -1) {System.out.print(new String(buffer, 0, len)); // 输出:你好,UTF-8!}} catch (IOException e) {e.printStackTrace();}}
}

注意:编码名称需严格匹配(如 "UTF-8" 而非 "utf8"),否则会抛出 UnsupportedEncodingException

四、总结与最佳实践

  1. 字符流适用场景:处理文本数据(如 .txt.csv.java),避免字节流处理文本时的手动编码转换。
  2. 核心类关系Reader/Writer 是抽象父类,FileReader/FileWriter 是文件处理实现类,InputStreamReader/OutputStreamWriter 是带编码控制的转换流。
  3. 编码最佳实践
    • 优先使用 UTF-8 编码(跨平台兼容性好)。
    • 避免使用 FileReader/FileWriter(默认编码易导致乱码),推荐用 InputStreamReader/OutputStreamWriter 并显式指定编码。
    • 读写同一文件时,确保编码一致。
  4. 资源管理:始终用 try-with-resources 语法关闭流,避免资源泄露。

掌握字符流的核心原理和编码规则,能让你在处理文本文件时游刃有余,远离乱码烦恼。下一篇将讲解缓冲字符流(BufferedReader/BufferedWriter),进一步提升 IO 效率。

http://www.dtcms.com/a/532255.html

相关文章:

  • C++新特性—— 智能指针(shared_ptr/unique_ptr/weak_ptr)
  • OpenCV(十四):绘制直线
  • 支持支付宝登录的网站建设wordpress指定分类文章列表
  • Halcon卡尺 Measure_pos原理与实现(C++ 和Python版本,基于OpenCV)
  • 在线课程网站开发任务书邢台 网站建设
  • 专业做英文网站如何看出一个网站优化的是否好
  • PostgreSQL事务隔离级别详解
  • 从底层逻辑到实战落地:服务端与客户端负载均衡器的深度拆解
  • 笔试强训(十三)
  • 基于罗伊适应模型的产后抑郁家庭护理干预研究综述​
  • Bright Data 抓取浏览器API实战:助力高效完成定向大规模数据稳定采集
  • 合肥市做效果图的网站最适合新人的写作网站
  • 关于做视频网站的一些代码南京网站制作公司怎么样
  • 6分钟制作TikTok游戏领域热门短视频分析AI Agent
  • 自由通讯的魔法:Go从零实现UDP/P2P 聊天工具
  • Cortex-M3-STM32F1 开发:(十二)HAL 库开发 ➤ SysTick 系统滴答定时器
  • go-ethereum core之以太网虚拟机EVM
  • 自己怎么免费做网站网站开发 合同
  • 网站如何做脚注一般使用的分辨率的显示密度是多少dpi )
  • 嵌入式开发中ln命令使用指南
  • C++模板进阶及特化实战指南
  • zenm自己做网站淮北建设网
  • 网站title keyword descriptionwordpress 分类筛选
  • 网站系统设计目标企业融资方案范本
  • 《AI 应用层革命(二)——从应用到生态:当智能体开始重塑世界》
  • 使用 Python 元类与属性实现惰性加载:Effective Python 第47条
  • 环广西世巡赛开战!维乐Senso Prime 坐垫助你竞速
  • DeepSeek讲“南辕北辙”者的志向
  • 做网站在线视频如何添加湘潭网站seo
  • 智能文本抽取:通过OCR、自然语言处理等多项技术,将非结构化文档转化为可读、可分析的数据资产