字符流
字符流
Java采用Unicode规范处理字符,字符流IO自动将Java内部字符格式与本地字符集进行转换,自动解
码编码,因此比起直接使用IO字节流,字符流操作相对更简单高效,所有的字符操作流都是从抽象类
Reader以及Writer继承而来。
字符编码
字符都有编码,读写字符时的编码不一致,则容易出现乱码,因此首先需要简单了解下常⻅的字符编
码规则。
ASCII码
ASCII是American Standard Code for Information Interchange缩写,称为美国信息交换标准代码。
ASCII一共定义了 128 个字符,其中 33 个字符是不可显示的控制字符, 95 个可显示的字符。
一个字节占 8 位,2^8=256,即一个字节可表示 256 个字符(0255),而ASCII字符只有 128 个,因此一个字节的低 7 位,2^7=128,足以表达全部的ASCII字符(0127),128~255则预留扩展其它字符,但 128 位根本不足以表示其它国家的字符。
GBK
- GBK全称汉字内码扩展规范,GBK一共收集了^2 万多汉字与字符,一个中文字符编码成两个字节进
行存储。 - GBK兼容了ASCII字符集
GBK是从GB2312编码规范上扩展的,而中国的汉字与符号有数十万,显然GBK也是不够的,因此又出现了GB18030, 按照字符集表示范围GB18030 > GBK > GB2312。
Unicode
各个国家都有自己的编码,当计算机信息在国际上进行交换时,就会出现问题,如用GBK编码的字节
数据发送给A国家,A国家采用A国码解码肯定就出现了乱码,此时国际标准组织就制定了一套通用的字
符集Unicode,即统一码,也叫万国码。
- Unicode字符集收纳了世界上所有文字、符号,统一进行编号
UTF-8
Unicode只是一种字符集,并不是编码方案,没有编码方案则无法存储。Unicode字符集出现最早的编
码方案是UTF-32,它规定所有的字符都采用固定的 4 个字节来表示, 4 个字节3 2位,可以表示 42 亿字
符,足以支撑Unicode字符集。即使只需一个字节的ASCII字符a(二进制0110 0001 ),也必须用 4 个字
节表示,前三字节直接补 0 。
UTF-32采用固定字节编码,程序处理简单,但是占用空间太大,基本很少使用,此时国际标准组织推出了Unicode编码方案UTF-8。
- UTF-8针对Unicode字符集采取可变⻓编码方案,共分为四个⻓度区,1~4个字节
- 英文、数字等只占用一个字节(兼容标准的ASCII编码),汉字字符占用^3 个字节
Reader
Reader读取字符流,子类必须实现read、close方法,大部分子类会覆盖Reader中方法,提供更高效
的操作或者一些额外的功能。
Reader提供的方法
1.4.3 Writer
Writer用于将字符写入到字符流中,子类必须实现write、flush、close方法,大部分子类会覆盖Writer
中方法,提供更高效的操作或者一些额外的功能。Note: 下图中少了一个常用的PrintWriter
操作使用
一个字符流通常包装一个字节流,通过字节流去实现底层物理IO操作,字符流处理字符与字节之间的数据转换。在Java中有两个通用的字节到字符的桥接流,InputStreamReader与 OutputStreamReader。
public class CopyCharacters { |
字符流编码
上述main方法在构造字符输入输出流时,未明确指定读写字符编码,字符流最终采用的是系统平台默
认编码。
- Windows中文系统默认是GBK
- Linux系统默认是UTF-8
Java在安装时,根据系统编码自动设置Java系统属性,通过如下命令查看Java系统属性
1 java -XshowSettings:property -version |
Java系统属性值中有 2 个与编码相关的属性
- file.encoding :这个非常重要,在Java中读取文件、URLEncode、字符串数据编码等都与此属性
有关。 - sun.jnu.encoding:不用关注,用于JVM查找加载class的类名路径编码等
注意:在Windows下开发时,使用Java命令查看Java系统属性时,file.encoding=GBK,当使用IDEA
工具开发项目时,通过IDEA给项目又设置了UTF-8编码,最终IDEA运行Java程序时,会通过-Dfile.encoding=UTF-8去覆盖默认的编码。当开发过程中遇⻅字符乱码时,需要关注JVM实际运行时的系统属性,也可通过代码获取。
1 String fileEncoding = System.getProperty("file.encoding") |
指定字符编码构造字符输入输出流
// 指定UTF-8编码读字符数据 |
读写字符数据时编码必须保持一致,否则会出现字符乱码。
在中文Windows上使用记事本新建D://test/source.txt文件,写入 2023,星云训练营 ,然后以ANSI编码 保存,在Windows中文系统上ANSI处理中文时就是GBK。
文件读写字符流
Java中还提供了FileReader、FileWriter简化了从文件读写字符,其内部自动封装包裹了对应的文件字节流,但是采用FileReader、FileWriter时只能使用JVM默认编码,无法单独设置读取字符的编码,因 此统一编码非常重要。
// InputStreamReader从文件读取字符内容 |
使用FileReader、FileWriter读写字符内容
public class CopyFileCharacters { |
如果需要追加内容到文件中,请使用FileWriter两个参数的构造方法,第二个参数设置为true
public FileWriter(File file, boolean append) |
字符流IO经常会以更大单位读取字符,最常用的就是按行读取字符。一行包括一系列字符组成的字符串以及末尾的行结束符,行结束符可以是回⻋换行符\r\n,也可以是单个回⻋键字符\r,或者单个换行符\n。不同的操作系统,其换行符可能有所不同。
- Dos、Windows采用回⻋+换行符(CR+LF)表示下一行,即字符表现形式\r\n
- Unix、Linux采用换行符(LF)表示下一行,字符表现形式为\n
- Mac采用回⻋符(CR)表示下一行,字符表现形式为\r
CR回⻋符ascii码十进制为13, 换行符ascii码十进制为 10 。
支持按行读写的字符流有BufferedReader、BufferedWriter、PrintWriter等,根据操作系统自动处理行结束符。