转换流
- 1. 概述
- 2. 作用
- 3. 字符编码和字符集
- 3.1 字符编码
- 3.2 字符集
- 4. `InputStreamReader`字符转换输入流
- 4.1 构造方法
- 4.2 代码示例
- 5. `OutputStreamWriter`字符转换输出流
- 5.1 构造方法
- 5.2 代码示例
- 6. 练习
- 6.1 练习1:转换文件编码
- 6.2 练习2:读取文件数据
- 7. 注意事项
文章中的部分照片来源于哔站
黑马程序员阿伟老师
处,仅用学习,无商用,侵权联系删除!
1. 概述
转换流是 Java 中用于在字节流和字符流之间进行转换的一种特殊流。
在处理数据时,有时候需要将字节流转换为字符流或字符流转换为字节流,这时就可以使用转换流来实现这种转换。转换流通常用于处理不同类型的数据,比如将字节流转换成字符流,或者将字符流转换成字节流。
在 Java 中,提供了两种主要的转换流:
-
InputStreamReader:
InputStreamReader
是用于将字节输入流转换为字符输入流的类。它接受一个字节输入流(如FileInputStream
或Socket.getInputStream()
)并根据指定的字符编码(如 UTF-8、GBK 等)将字节数据转换为字符数据。 -
OutputStreamWriter:
OutputStreamWriter
是用于将字符输出流转换为字节输出流的类。它接受一个字节输出流(如FileOutputStream
或Socket.getOutputStream()
)并根据指定的字符编码将字符数据转换为字节数据。
2. 作用
-
字节流和字符流的转换:转换流允许将字节流转换为字符流,以便更方便地处理文本数据。通过InputStreamReader将字节输入流转换为字符输入流,通过OutputStreamWriter将字符输出流转换为字节输出流,使得在处理文本数据时更加方便。
-
字符编码转换:转换流提供了指定字符编码的功能,可以在转换流的构造方法中指定具体的字符编码,从而确保在字节和字符之间转换时使用指定的编码规则,避免因编码不一致导致的乱码等问题。
-
处理不同数据格式:转换流可以帮助处理来自外部数据源的字节数据,将其转换为字符数据进行处理,或者将处理完的字符数据转换为字节数据输出到外部数据源中,使得处理不同格式数据更加灵活。
3. 字符编码和字符集
3.1 字符编码
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。
反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
编码
:字符(能看懂的)–字节(看不懂的)
解码
:字节(看不懂的)–>字符(能看懂的)
-
字符编码
Character Encoding
: 就是一套自然语言的字符与二进制数之间的对应规则。编码表:生活中文字和计算机中二进制的对应规则
3.2 字符集
- 字符集
Charset
:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
- ASCII字符集 :
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
- 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
- ISO-8859-1字符集:
- 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
- ISO-8859-1使用单字节编码,兼容ASCII编码。
- GBxxx字符集:
- GB就是国标的意思,是为了显示中文而设计的一套字符集。
- GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
- GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
- GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
- Unicode字符集 :
- Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
- 它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
- UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
- 128个US-ASCII字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的Unicode辅助字符,使用四字节编码。
4. InputStreamReader
字符转换输入流
4.1 构造方法
-
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。 -
InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。构造举例,代码如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt")); InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
4.2 代码示例
-
代码示例
需求:手动创建一个GBK的文件,把文件中的中文读取到内存中,不能出现乱码GBK文件: D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream01\GBK01.txt
package text.IOStream.ConvertStream.ConvertStream01; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; /*转换文件编码 需求:手动创建一个GBK的文件,把文件中的中文读取到内存中,不能出现乱码 GBK文件: D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream01\GBK01.txt */ public class ConvertStream01 { public static void main(String[] args) throws IOException { //原始方法(了解就行) System.out.println("原始方法:"); method1(); System.out.println(); //JDK11以后得方法(需要掌握) //在JDK11以后java在FileReader的构造方法中添加了InputStreamReader字符转换流的对象 System.out.println("JDK11以后得方法:"); method2(); } //原始方法(了解就行) public static void method1() throws IOException { //创建转换输入流对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream01\\GBK01.txt"), "GBK"); //读取数据 int b; while ((b = isr.read()) != -1) { System.out.print((char) b); } //释放资源 isr.close(); } //JDK11以后得方法(需要掌握) public static void method2() throws IOException { //创建输入流对象 FileReader fis = new FileReader("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream01\\GBK01.txt", Charset.forName("GBK")); //读取数据 int b; while ((b = fis.read()) != -1) { System.out.print((char) b); } //释放资源 fis.close(); } }
-
输出结果
- GBK文件(因为idea默认是UTF-8,因此会出现乱码形象)
- 原始方法
- JDK11以后方法
- GBK文件(因为idea默认是UTF-8,因此会出现乱码形象)
5. OutputStreamWriter
字符转换输出流
5.1 构造方法
-
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。 -
OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。构造举例,代码如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt")); OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
5.2 代码示例
- 代码示例
需求:把一段中文按照GBK的方式写到本地文件package text.IOStream.ConvertStream.ConvertStream02; import java.io.*; import java.nio.charset.Charset; /*转换文件编码 需求:把一段中文按照GBK的方式写到本地文件 */ public class ConvertStream02 { public static void main(String[] args) throws IOException { //原始方法(了解就行) System.out.println("原始方法:"); method1(); System.out.println(); //JDK11以后得方法(需要掌握) //在JDK11以后java在FileWriter的构造方法中添加了InputStreamWriter字符转换流的对象 System.out.println("JDK11以后得方法:"); method2(); } //JDK11以后得方法(需要掌握) private static void method2() throws IOException { //创建对象 FileWriter fw = new FileWriter("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream02\\a.txt", Charset.forName("GBK")); //输出数据 String str = "星光闪烁,如同我们之间的誓言,\n" + "余生余生,只愿与你相伴。"; fw.write(str); //释放资源 fw.close(); } //原始方法(了解就行) private static void method1() throws IOException { //创建对象 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream02\\a.txt"), "GBK"); //输出数据 String str = "星光闪烁,如同我们之间的誓言,\n" + "余生余生,只愿与你相伴。"; osw.write(str); //释放资源 osw.close(); } }
- 输出结果
- ieda里展示的文件(因为idea默认是UTF-8,因此会出现乱码形象)
- 记事本中打开(记事本默认是GBK)
- ieda里展示的文件(因为idea默认是UTF-8,因此会出现乱码形象)
6. 练习
6.1 练习1:转换文件编码
- 需求:将本地文件中的GBK文件,转成UTF-8
GBK文件:D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream03\GBK01.txt
UTF-8:D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream03\UTF8 - 代码示例
package text.IOStream.ConvertStream.ConvertStream03; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; /*转换文件编码 需求:将本地文件中的GBK文件,转成UTF-8 GBK文件:D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream03\GBK01.txt UTF-8:D:\JavaCode\code\codeText01\src\text\IOStream\ConvertStream\ConvertStream03\UTF8 */ public class ConvertStream03 { public static void main(String[] args) throws IOException { //创建对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream03\\GBK01.txt"), "GBK"); FileWriter fw = new FileWriter("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream03\\UTF8", Charset.forName("UTF-8")); //输入数据 int b; while ((b = isr.read()) != -1) { //输出数据 fw.write((char) b); } //释放资源 fw.close(); isr.close(); } }
- 输出结果
- GBK文件(因为idea默认是UTF-8,因此会出现乱码形象)
- UTF-8文件
- GBK文件(因为idea默认是UTF-8,因此会出现乱码形象)
6.2 练习2:读取文件数据
- 需求:利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
- 代码示例
package text.IOStream.ConvertStream.ConvertStream04; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; /*读取文件中的数据 需求:利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码 */ public class ConvertStream04 { public static void main(String[] args) throws IOException { //创建转换流对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\JavaCode\\code\\codeText01\\src\\text\\IOStream\\ConvertStream\\ConvertStream04\\a.txt")); // 创建缓冲流对象,从而调用readline方法,每次读取一行 BufferedReader br = new BufferedReader(isr); //读取数据 String line; while ((line = br.readLine()) != null) { System.out.println(line); } //释放资源 br.close(); isr.close(); } }
- 输出结果
- a.txt
- 程序输出
- a.txt
7. 注意事项
-
字符编码一致性:在创建转换流时,应该明确指定字符编码,确保输入流和输出流之间的字符编码保持一致。不同的字符编码可能会导致数据解析错误或产生乱码。
-
异常处理:在使用转换流时,需要及时捕获和处理可能抛出的异常,如字符编码不支持等异常,以避免程序崩溃或产生不可预料的错误。
-
适当的缓冲处理:转换流本身并不具备缓冲功能,如果需要使用缓冲功能以提高性能,可以考虑在转换流外包装一层缓冲流(如BufferedReader 或 BufferedWriter)来增加缓冲效果。
-
流的关闭:在不再需要使用转换流时,应该手动关闭转换流,以释放资源。可以使用 try-with-resources 结构确保转换流在使用完毕后能够被正确关闭。
-
性能考量:转换流可能会引入一定的性能开销,特别是在大规模数据处理时,需要对性能进行适当的评估和优化。