Java --> IO流(字符集)

2022/7/10 1:22:31

本文主要是介绍Java --> IO流(字符集),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

 

  • 字符集:
 1 import java.util.Arrays;
 2 
 3 public class Test {
 4     public static void main(String[] args) throws Exception {
 5         //1、编码:把文字转换成指定的字节
 6         String name = "abc阿1百2川";
 7         byte[] bytes = name.getBytes(); //以当前代码默认字符集进行编码(UTF-8)
 8         System.out.println(bytes.length); //一个英文字符占1个字节,一个中文字符占3个字节
 9         System.out.println(Arrays.toString(bytes));
10 
11         System.out.println("---------------");
12         //以指定形式(GBK)转换成字节数组
13         byte[] bytes1 = name.getBytes("GBK"); //一个英文字符占1个字节,一个中文字符占2个字节
14         System.out.println(bytes1.length);
15         System.out.println(Arrays.toString(bytes));
16 
17         //2、解码:把字节转换成对应的中文形式(注:编码前和编码后的字符集要一致)
18         String str = new String(bytes); //以当前代码默认字符集进行解码(UTF-8)<编码、解码前后字符集一致>
19         System.out.println(str);
20 
21         System.out.println("-----------------");
22         String str1 = new String(bytes1); //以当前代码默认字符集进行解码(UTF-8)<编码(GBK)、解码(UTF-8)前后字符集不一致>
23         System.out.println(str1);
24         System.out.println("---乱码原因:GBK以两个字节存储的数据会被UTF-8强行解析成三个字节表示的数据---");
25         //改正:编码使用GBK、解码也要使用GBK
26         String str2 = new String(bytes1, "GBK");
27         System.out.println(str2);
28     }
29 }

  •  文件字节输入流(FileInputStream):每次读取一个字节(适合读英文,读取中文会发生乱码)
  • 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去
 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.InputStream;
 4 
 5 public class FileInputStreamDemo1 {
 6     public static void main(String[] args) throws Exception{
 7         //创建文件字节输入流管道与源文件对象接通
 8         //InputStream inputStream = new FileInputStream(new File("file-io-app/src/data.txt")); //多态
 9         //简化写法:可以直接定位到文件
10         InputStream inputStream = new FileInputStream("file-io-app/src/data.txt"); //多态
11 
12         //读取一个字节返回
13 //        int read = inputStream.read();
14 //        System.out.print(read + " ");  //97
15 //        System.out.println((char)read);  //a
16 //
17 //        int read1 = inputStream.read();
18 //        System.out.print(read1 + " ");  //98
19 //        System.out.println((char)read1); //b
20 //
21 //        int read2 = inputStream.read();
22 //        System.out.print(read2 + " "); //99
23 //        System.out.println((char)read2); //c
24 //
25 //        int read3 = inputStream.read();
26 //        System.out.print("读取不到返回:" + read3); // -1
27 
28         //循环:弊端 --> 读取中文时会出现乱码:按照3个字节来读取<会截断原有的字节数据>
29         int read = 0;
30         while ((read = inputStream.read()) != -1){  
31             System.out.print((char) read);
32         }
33     }
34 }

  • 文件字节输入流(FileInputStream):每次读取一个字节数组
 1 import java.io.FileInputStream;
 2 import java.io.InputStream;
 3 
 4 //使用文件字节输入流每次读取一个字节数组的形式
 5 public class FileInputStreamDemo2 {
 6     public static void main(String[] args) throws Exception{
 7         //创建文件字节输入流管道与源文件接通
 8         InputStream inputStream = new FileInputStream("file-io-app/src/data02.txt");
 9         //定义字节数组,用于读取字节数组
10         byte[] bytes = new byte[3]; //声明字节数组大小,即每次读取的字节数
11 
12 //        int read = inputStream.read(bytes);
13 //        System.out.println(read);
14 //        //解码(UTF-8)
15 //        String str = new String(bytes);
16 //        System.out.println(str);
17 //
18 //        int len1 = inputStream.read(bytes);
19 //        System.out.println(len1);
20 //        //解码(UTF-8)
21 //        String str1 = new String(bytes);
22 //        System.out.println(str1);
23 //
24 //        int len2 = inputStream.read(bytes);
25 //        System.out.println(len2);
26 //        //解码(UTF-8)
27 //        String str2 = new String(bytes);
28 //        System.out.println(str2);
29 //
30 //        //读取几个倒几个
31 //        int len3 = inputStream.read(bytes);
32 //        System.out.println(len3);
33 //        //解码(UTF-8)
34 //        String str3 = new String(bytes,0,len3);
35 //        System.out.println(str3);
36 //
37 //        int len4 = inputStream.read(bytes);
38 //        System.out.println(len4);
39 
40         //改进:使用循环,每次读取一个字节数组
41         //优点:性能提升(相较于一个一个字节而言,使用字节数组更快)、做复制时没问题
42         //弊端:中英文混合时会发生乱码
43         int length = 0;
44         while ((length = inputStream.read(bytes,0,3)) != -1){
45             String str = new String(bytes,0,length);
46             System.out.print(str);
47         }
48     }
49 }

示例代码运行结果:

 

 示例代码之所以没有出现乱码的原因:abc刚好够一个字节数组,而一个中文又占3个字节,最后两个字符12读取多少个截取多少个。

  • 文件字节输入流:一次读完全部的字节

如何使用字节输入流读取中文内容不乱码?

定义一个与文件一样大的字节数组,一次性读完文件的所有字节。不过这样做也有相应的问题:如果文件过大,字节数组可能引起内存溢出。

  • 方式一:自己定义一个与文件一样大的字节数组
  • 方式二:官方提供了readAllBytes()的API 【JDK9开始】
 1 public class FileInputStreamDemo3 {
 2     public static void main(String[] args) throws Exception{
 3         File file = new File("file-io-app/src/data03.txt");
 4         //创建文件字节输入流管道与源文件接通
 5         InputStream inputStream = new FileInputStream(file);
 6         //定义字节数组,用于读取字节数组
 7         int length = (int)file.length();
 8         byte[] bytes = new byte[length]; //声明字节数组大小,即每次读取的字节数
 9         int charNumber = inputStream.read(bytes);
10         String str = new String(bytes,0,length);
11         System.out.println("读取了:" + charNumber + "个字节");
12         System.out.println(str);
13 
14         //不过自JDK9开始,Java也提供了可以一次性读完所有文件内容的API
15 //        byte[] bytes = inputStream.readAllBytes();
16 //        System.out.println(new String(bytes));
17     }
18 }

 

 示例代码运行结果:

  • 文件字节输出流:写字节数据到文件
 1 import java.io.FileOutputStream;
 2 
 3 public class FileOutputStreamDemo1 {
 4     public static void main(String[] args) throws Exception {
 5         //1、创建文件字节输出流管道与目标文件接通
 6         //覆盖管道:先清空之前的管道,在写入新的数据
 7         FileOutputStream outputStream = new FileOutputStream("file-io-app/src/out04.txt");
 8         //如果想追加管道,只需在构造器后多加一个参数true
 9         //FileOutputStream outputStream = new FileOutputStream("file-io-app/src/out04.txt",true);
10         //2、写数据出去
11         outputStream.write('a');
12         outputStream.write(98); //对应字符的ASCII码值
13         outputStream.write('c');
14         outputStream.write("\r\n".getBytes()); //换行
15         //写入中文会发生乱码的原因:默认只能写一个字节,而一个中文占3个字节,写入中文时会强行地把中文字符的第一个字节截断
16         //outputStream.write('人');
17         //以GBK编码的形式写入出现乱码的原因:编码用的时GBk,解码时用的时UTF-8
18 //        byte[] bytes = "闻道有先后".getBytes("GBK");
19 //        outputStream.write(bytes);
20 
21         //3、写一个字节数组的一部分出去
22         byte[] bytes = {'a',97,98,99}; //只写前三个
23         outputStream.write(bytes,0,3);
24 
25         //刷新数据【写数据必须刷新数据】
26         outputStream.flush(); //刷新后流还可以继续用
27         outputStream.close(); //关闭后流就不可以用了,且关闭之前会自动刷新流
28     }
29 }

data04.txt文件内容:

  •  使用字节输入、输出流做文件的拷贝
 1 import java.io.*;
 2 
 3 //使用字节流完成文件的复制
 4 public class CopyDemo5 {
 5     public static void main(String[] args) {
 6         //创建字节输入流管道与原视频接通
 7         try {
 8             InputStream inputStream = new FileInputStream("D:\\tencent\\QQ\\MobileFile\\class_for_183.mp4");
 9             //创建字节输出流管道与目标文件接通
10             OutputStream outputStream = new FileOutputStream("D:\\tencent\\QQ\\MobileFile\\new_class_for_183.mp4");
11             //定义一个字节数组转移数据
12             byte[] bytes = new byte[1024];
13             int len = 0; //len 用于记录每次读取到的数据个数(读多少倒多少,防止最后的一组数据装不满)
14             while ((len = inputStream.read(bytes)) != -1 ){
15                 outputStream.write(bytes,0,len);
16             }
17             System.out.println("复制完成");
18             //关闭流
19             inputStream.close();
20             outputStream.close();
21         } catch (Exception e) {
22             e.printStackTrace();
23         }
24     }
25 }

因为任何文件的底层都是字节,拷贝则是对这些字节进行转移,只要前后文件格式、编码一致就没有任何问题。

  • 资源释放的两种方式(第一种):try-catch-finally

 因此,对于上述代码建议更改为下列模式的写法:

 1 import java.io.*;
 2 
 3 public class TryCatFinallyDemo1 {
 4     public static void main(String[] args) {
 5         //把对象定义到上边
 6         InputStream inputStream = null;
 7         OutputStream outputStream = null;
 8         //创建字节输入流管道与原视频接通
 9         try {
10             //以防出现在代码块上边出现异常导致两个管道都为null ,如:此处出现 System.out.prinln(10 / 0);
11             inputStream = new FileInputStream("D:\\tencent\\QQ\\MobileFile\\class_for_183.mp4");
12             //创建字节输出流管道与目标文件接通
13             outputStream = new FileOutputStream("D:\\tencent\\QQ\\MobileFile\\new_class_for_183.mp4");
14             //定义一个字节数组转移数据
15             byte[] bytes = new byte[1024];
16             int len = 0; //len 用于记录每次读取到的数据个数(读多少倒多少,防止最后的一组数据装不满)
17             while ((len = inputStream.read(bytes)) != -1 ){
18                 outputStream.write(bytes,0,len);
19             }
20             System.out.println("复制完成");
21 
22 
23         } catch (Exception e) {
24             e.printStackTrace();
25         }finally {
26             //关闭流
27             try {
28                 if (inputStream != null) inputStream.close();
29             } catch (IOException e) {
30                 e.printStackTrace();
31             }
32             try {
33                 if (outputStream != null) outputStream.close();
34             } catch (IOException e) {
35                 e.printStackTrace();
36             }
37         }
38     }
39 }
System.exit(0) ; //try代码块里边干掉虚拟机,finall代码块就不会执行了
  • 资源释放的两种方式(方式二):try-with-resource

 正如上述代码所示(27~36行):如果要关闭两个管道的流,就需要写两个try - catch代码块,写法较为繁琐。

 1 import java.io.*;
 2 
 3 public class TryWithResourceDemo1 {
 4     public static void main(String[] args) {
 5         //创建字节输入流管道与原视频接通
 6         try(
 7                 //这里边防止资源对象,用完后会自动关闭(即使出现异常)
 8                 //以防出现在代码块上边出现异常导致两个管道都为null ,如:此处出现 System.out.prinln(10 / 0);
 9                 InputStream inputStream = new FileInputStream("D:\\tencent\\QQ\\MobileFile\\dest_video.mp4");
10                 //创建字节输出流管道与目标文件接通
11                 OutputStream outputStream = new FileOutputStream("D:\\tencent\\QQ\\MobileFile\\dest_video.mp4");
12 
13                 Myconnection myconnection = new Myconnection()
14                 //int age = 9; //这样的代码防止在这里是不被允许的,只能放置资源,而所谓的资源,就是实现了AutoCloseable接口
15                 ) {
16             //定义一个字节数组转移数据
17             byte[] bytes = new byte[1024];
18             int len = 0; //len 用于记录每次读取到的数据个数(读多少倒多少,防止最后的一组数据装不满)
19             while ((len = inputStream.read(bytes)) != -1 ){
20                 outputStream.write(bytes,0,len);
21             }
22             System.out.println("复制完成");
23         } catch (Exception e) {
24             e.printStackTrace();
25         }
26     }
27 }
28 
29 class Myconnection implements AutoCloseable{
30     //重写接口里边的close方法
31     @Override
32     public void close() throws Exception {
33         //此方法被调用了就说明是下了AutoCloseable的接口就会自动调用此处的close方法,也就是释放资源
34         System.out.println("资源被成功释放!");
35     }
36 }

 

 JDK9提供的释放资源的方式:

 

 这样做就很尴尬了,虽说把资源管道定义在了try代码块外边,但是外边的管道又需要把异常抛出,那么既如此,而下边的try-catch代码块就是用来捕获异常的,就起不到作用了~

  • 文件字符输入流、文件字符输出流
  • 一个一个字符进行读取
 1 import java.io.FileReader;
 2 import java.io.Reader;
 3 
 4 public class FileReaderDemo1 {
 5     public static void main(String[] args) throws Exception{
 6         //每次读取一个字符
 7         //1、创建一个字符输入流管道与源文件对象接通
 8         Reader reader = new FileReader("file-io-app\\src\\data05.txt");
 9 
10         //2、每次读取一个字符
11 //        int len1=  reader.read(); //返回的是对应字符的编号,没有可读的字符时返回-1
12 //        System.out.println("读取到字符编号:" + len1);  //49
13 //        System.out.println("读取到的字符:" + (char) len1);  //1
14 //        System.out.println("------------------------");
15 //        int len2 = reader.read();
16 //        System.out.println("读取到字符编号:" + len2); //46
17 //        System.out.println("读取到的字符:" + (char) len2); //.
18 //        System.out.println("------------------------");
19 //        int len3 = reader.read();
20 //        System.out.println("读取到字符编号:" + len3); //32
21 //        System.out.println("读取到的字符:" + (char) len3); //空格
22 
23         //使用循环的方式读取
24         int len = 0;
25         while ((len = reader.read()) != -1){
26             System.out.print( (char) len) ;
27         }
28     }
29 }

data05.txt文件内容:

 示例程序运行结果:

 注:在此,需要指出的一个小小的点是 -- 有人可能会问,char类型的数据不是底层占2个字节吗,而在Unicode编码中,中文字符是占3个字节的,为什么可以用char来读取中文字符呢?需要阐明的是:这个地方使用的是字符输入流,也就是说 int len = reader.read(),其中 len 作为read 的返回值,源码中是这样描述的:The number of characters read, or -1 if the end of the stream has been reached

即:读取到的字符数,如果到流的末尾了则返回 -1 。也就是说,字符流读取数据是以字符为最小单位进行读取的,读取到的是字符(所代表的Unicode编码值),只是最终结果把该字符的编码值强制转换成了实际所代表值。

  • 字符数组进行读取
 1 import java.io.FileReader;
 2 import java.io.Reader;
 3 
 4 //一个一个字符数组读取
 5 public class FileReaderDemo2 {
 6     public static void main(String[] args) throws Exception{
 7         //1、创建文件字节输入流管道与源文件接通
 8          Reader reader = new FileReader("file-io-app/src/data06.txt");
 9 
10          //2、声明循环,每次读取一个字符数组的数据
11         char[] buffer = new char[1024]; //每次读取1k的数据
12 
13         //3、定义一个len记录每次读取到的字符个数
14         int len = 0;
15 
16         //4、循环读取
17         while ((len = reader.read(buffer)) != -1){
18             String str = new String(buffer,0,len);
19             System.out.print(str);
20         }
21     }
22 }

 data06.txt文档内容(这里为了省事就直接把FileReaderDemo1中的代码粘贴进去了)就不在此展示了,直接展示程序运行结果:

 

  •  文件字符输出流
 1 import java.io.FileWriter;
 2 import java.io.Writer;
 3 
 4 public class FileWriteDemo3 {
 5     public static void main(String[] args) throws Exception{
 6         //覆盖管道:每次运行都会清空之前的数据(输出流如果不存在out08.txt会自动创建)
 7         Writer writer = new FileWriter("file-io-app/src/out08.txt");
 8         //追加管道:每次运行都是追加之前的数据,对源数据不会造成影响
 9         //Writer writer = new FileWriter("file-io-app/src/out08.txt", true);
10 
11         //1、写一个字符
12         writer.write(97);
13         writer.write(98);
14         writer.write('鹏');
15         writer.write("\r\n"); //换行
16 
17         //2、写一个字符串
18         writer.write("闻道有先后,术业有专攻");
19         writer.write("\r\n");
20 
21         //3、写一个字符数组出去
22         //char[] chars = {'J','a','v','a'};
23         char[] chars = "Java".toCharArray();
24         writer.write(chars);
25         writer.write("\r\n");
26 
27         //4、写字符串数组的一部分出去
28 
29         /**
30          * off : 开始写入字符的偏移量,(可以理解为字符出的下标)
31          * len : 写入的字符个数
32          */
33 
34         writer.write("一分耕耘一分收获",0,4);
35         writer.write("\r\n");
36 
37         //5、写字符数组的一部分
38         char[] chars1 = "一二三四五六七八九十".toCharArray();
39         writer.write(chars1,2,5); // 三四五六七
40         writer.write("\r\n");
41 
42         //刷新流
43         //writer.flush(); //刷新后流还可以继续使用
44         //关闭流
45         writer.close(); //关闭(会自动刷新流)后流就不可以使用了
46     }
47 }

示例程序运行后 out08.txt 的文档内容:



这篇关于Java --> IO流(字符集)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程