JAVA IO流中的转换流(字节流和字符流之间的转换)

1.概念

在java中,转化流(Transformation Stream)主要用于读取和写入文本数据,同时可以进行字符编码和字符集的转换。转换流主要有两类:InputStreamReader和OutputStreamWriter。这两个类都属于字符流。其中InputStreamReader将字节输入流转为字符输入流,继承自Reader。OutputStreamWriter是将字符输出流转为字节输出流,继承自Writer。

2.编码和解码 

众所周知,计算机中存储的数据都是二进制的数字,我们在电脑屏幕上看到的文字信息是将二进制转换之后显示的,两者之间存在编码解码的过程,其互相转换必须遵循某种规则,即编码和解码都遵循同一种规则才能将文字信息正常显示,如果编码跟解码使用了不同的规则,就会出现乱码的情况。

  • 编码:字符、字符串(能看懂的)--字节(看不懂的)
  • 解码:字节(看不懂的)-->字符、字符串(能看懂的)

 

常见的编码和解码方式有很多,举几个例子:

  • ASCII 编码和解码:在计算机中,常常使用 ASCII 码来表示字符,如键盘上的字母、数字和符号等。例如,字母 A 对应的 ASCII 码是 65,字符 + 对应的 ASCII 码是 43。
  • Unicode 编码和解码:Unicode 是一种字符集,支持多种语言和字符集。在计算机中,Unicode 可以使用 UTF-8、UTF-16 等编码方式将字符转换为二进制数据进行存储和传输。
  • Base64 编码和解码:Base64 是一种将二进制数据转换为 ASCII 码的编码方式。它将 3 个字节的二进制数据转换为 4 个 ASCII 字符,以便在网络传输中使用。例如,将字符串 "Hello, world!" 进行 Base64 编码后,得到的结果是 "SGVsbG8sIHdvcmxkIQ=="。
  • 图像编码和解码:在图像处理中,常常使用 JPEG、PNG、GIF 等编码方式将图像转换为二进制数据进行存储和传输。在解码时,可以将二进制数据转换为图像,以便显示或处理。
  • 视频编码和解码:在视频处理中,常常使用 H.264、AVC、MPEG-4 等编码方式将视频转换为二进制数据进行存储和传输。在解码时,可以将二进制数据转换为视频,以便播放或处理。

 下面是解码和编码的代码实现:

使用String类的getBytes和new String方法

JAVA的String类提供了getBytes方法,可以将字符串转换为字节序列,而new String构造函数可以将字节序列转回字符串。

public class Main {
    public static void main(String[] args) {
        String original ="Hello world";
        //编码:将字符串转换为字符串
        byte[] bytes =original.getBytes(StandardCharsets.UTF_8);
        System.out.println(Arrays.toString(bytes));
        //解码:将字节序列转换为字符串
        String decodeString =new String(bytes,StandardCharsets.UTF_8);
        System.out.println(decodeString);
    }
}

 使用Charset方法

我们首先使用getBytes()方法将字符串编码为GBK字节序列,然后只用new String(byte[] bytes,Charset charset)将字节序列解码回字符串

public class Main {
    public static void main(String[] args) {
        String original ="Hello world";
        //获取GBK字符集
        Charset gbkCharset =Charset.forName("GBK");
        //编码:将字符串转换为GBK字节序列
        byte[] gbkBytes =original.getBytes(gbkCharset);
        System.out.println(Arrays.toString(gbkBytes));
        //解码:将GBK字节序列转换为字符串序列
        String decodeString =new String(gbkBytes,gbkCharset);
        System.out.println(decodeString);
    }
}

3.字符集

在java中,字符集(Character Set)是指一组字符以及对应的编码。字符集允许计算机存储和处理文本数据。在java使用Unicode字符集作为内部字符和字符串的表示方式,这意味着java中char类型是16位的,可以表示Unicode字符集中的任何字符。

 

Unicode字符集

Unicode 包含了世界上几乎所有的字符,用于表示人类语言、符号和表情等各种信息。Unicode 字符集中的每个字符都有一个唯一的码点(code point),用于表示该字符在字符集中的位置,可以用十六进制数表示。

  • UTF-8 是一种可变长度的编码方式,对于 ASCII 字符(码点范围为 0x00~0x7F),使用一个字节表示,对于其他 Unicode 字符,使用两个、三个或四个字节表示。UTF-8 编码方式被广泛应用于互联网和计算机领域,因为它可以有效地压缩数据,适用于网络传输和存储。使用文字数据传输。
  • UTF-16 是一种固定长度的编码方式,对于基本多语言平面(Basic Multilingual Plane,Unicode 字符集中的一个码位范围,包含了世界上大部分常用的字符,总共包含了超过 65,000 个码位)中的字符(码点范围为 0x0000~0xFFFF),使用两个字节表示,对于其他 Unicode 字符,使用四个字节表示。
  • UTF-32 是一种固定长度的编码方式,对于所有 Unicode 字符,使用四个字节表示。

GBK字符集

GBK字符集是中国国家标准的GB13000的扩展,主要用于简体中文字符的编码。GBK全称是“汉字内码扩展规范”,它在GB2312的基础上增加了更多的汉字和符号,支持更多的字符。

  • 扩展性:GBK在FB2312的基础上添加了更多的汉字和符号,总共可以表示21000多个汉字和符号。
  • 兼容性:GBK和GB2312的基本兼容,GB2312中所有字符在GBK中都有相同的编码。
  • 双字节字符集:GBK是一个双字节字符集(Double_Byte Character Set)每个字符通常使用两个字节表示。
  • GB2312 的全名是《信息交换用汉字编码字符集基本集》,也被称为“国标码”。采用了双字节编码方式,每个汉字占用 2 个字节,其中高字节和低字节都使用了 8 位,因此 GB2312 编码共有 2^16=65536 种可能的编码,其中大部分被用于表示汉字字符。GB2312 编码中的每个字节都可以采用 0xA1 到 0xF7 之间的任意一个值,因此可以表示 126 个字符。
  • GB2312 是一个较为简单的字符集,只包含了常用的汉字和符号,因此对于一些较为罕见的汉字和生僻字,GB2312 不能满足需求,现在已经逐渐被 GBK、GB18030 等字符集所取代。
  • GB18030 是最新的中文码表。收录汉字 70244 个,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。

4.乱码

当使用不同的编码方式读取或者写入文件时,就会出现乱码问题,来看示例:

    public class Main {
        public static void main(String[] args) {
            String s = "大帅比!";
    
            try {
                // 将字符串按GBK编码方式保存到文件中
                OutputStreamWriter out = new OutputStreamWriter(
                        new FileOutputStream("fos.txt"), "GBK");
                out.write(s);
                out.close();
    
                FileReader fileReader = new FileReader("fos.txt");
                int read;
                while ((read = fileReader.read()) != -1) {
                    System.out.print((char)read);
                }
                fileReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }

在上面的示例代码中,首先定义了一个包含中文字符的字符串,然后将该字符串按 GBK 编码方式保存到文件中,接着将文件按默认编码方式(UTF-8)读取,并显示内容。此时就会出现乱码问题,显示为“��Ĭ������”。

这是因为文件中的 GBK 编码的字符在使用 UTF-8 编码方式解析时无法正确解析,从而导致出现乱码问题。

那如何才能解决乱码问题呢?

这就引出我们今天的主角了——转换流。

4.1 InputStreamReader

InputStreamReader这个类是从InputStream中读取字符流,并且能够将字节转换为字符,通过指定的字符编码。InputStreamReader是字符流和字节流之间的桥梁。

构造方法:

  • InputStreamReader(InputStream in):创建一个使用默认字符编码的InputStream,从指定的InputStream读取数据。
  • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

代码:

InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

下面是使用这个代码来解决问题:

    public class Main {
        public static void main(String[] args) {
            String s = "大帅比!";

            try {
                // 将字符串按GBK编码方式保存到文件中
                OutputStreamWriter out = new OutputStreamWriter(
                        new FileOutputStream("fos.txt"), "GBK");
                out.write(s);
                out.close();

                InputStreamReader isr = new InputStreamReader(new FileInputStream("logs/test_utf8.txt"), "GBK");
                int read;
                while ((read = isr.read()) != -1) {
                    System.out.print((char)read);
                }
                isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

4.2 OutputStreamWriter

java.io.OutputStreamWriter 是 Writer 的子类,字面看容易误以为是转为字符流,其实是将字符流转换为字节流,是字符流到字节流的桥梁。

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
  • OutputStreamWriter(OutputStream in, String charsetName):创建一个指定字符集的字符流。
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK");

下面是代码示例:

这里可以用缓冲流优化一下代码


    public class Main {
        public static void main(String[] args) {
          try{
              //从文件读取字节流,使用UTF-8编码方式
              FileInputStream fis =new FileInputStream("b.txt");
              //将字节流转换为字符流,使用UTF-8编码方式
              InputStreamReader isr =new InputStreamReader(fis,"UTF-8");
              //使用缓冲流包装字符流,提高读取效率
              BufferedReader br =new BufferedReader(isr);
              //创建输出流,使用UTF-8编码方式
              FileOutputStream fos =new FileOutputStream("output.txt");
              //将输出流包装为转换流,使用UTF-8的编码方式
              OutputStreamWriter osw =new OutputStreamWriter(fos,"UTF-8");
              //使用缓冲流包装转换流,提高写入效率
              BufferedWriter bw =new BufferedWriter(osw);
              
              //读取输入文件的每一行,写入懂啊输出文件中
              String line;
              while ((line=br.readLine())!=null){
                  bw.write(line);
                  bw.newLine();//每行结束之后写入一个换行符
              }
              br.close();
              bw.close();
          } catch (IOException e) {
              e.printStackTrace();
          }

        }
    }

在上面的示例代码中,首先使用 FileInputStream 从文件中读取字节流,使用 UTF-8 编码方式进行读取。然后,使用 InputStreamReader 将字节流转换为字符流,使用 UTF-8 编码方式进行转换。接着,使用 BufferedReader 包装字符流,提高读取效率。然后,创建 FileOutputStream 用于输出文件,使用 UTF-8 编码方式进行创建。接着,使用 OutputStreamWriter 将输出流转换为字符流,使用 UTF-8 编码方式进行转换。最后,使用 BufferedWriter 包装转换流,提高写入效率。

总结:

  • 输入流:输入流是程序从中读取数据的源
  • 输出流:输出流是程序写入数据的目的地
  • 转换流:转换流位于输入流和输出流之间,它读取输入流中的数据,转换数据,然后将转换后的数据写入输出流中

工作原理: 

  • InputStreamReader 和 OutputStreamWriter:这两个类是字节流和字符流之间的桥梁。InputStreamReader 可以将字节流转换为字符流,而 OutputStreamWriter 可以将字符流转换为字节流。
  • BufferedReader 和 BufferedWriter:这些类提供缓冲功能,可以提高读写效率,并且可以与转换流结合使用。

 

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值