数据读写API——IO流

理清一些概念

1.Java 中的IO是干啥的?

IO指的是Input和Output,主要目的是实现数据在存储介质之间的传输。【流:数据流,类比与水流的流动】

2.IO分类

按照操作单元来划分,可以分为字节流和字符流。

4.字节流和字符流的区别

字节流顾名思义操作的数据单元是1个字节,也就是8位;

那么问题来了,一个中文字符用GBK编码占用两个字节,用UTF-8占用三个字符。

字节流在传输有中文字符的数据时会产生编码错误。

而字符流可操作2个字节,也就是16位。那么在GBK的编码下,支持中文数据传输。

总结如下

  • 字节流一次读取一个字节,字符流两个
  • 字节流可以处理所有类型数据,字符流只能处理字符类数据

4.字符编码的前世今生

ASCII 码

计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有01两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。

也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000011111111

上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。

【ASCII 码==英文字符和二进制之间的映射关系】

Unicode

世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。电子邮件常常出现乱码就是因为发信人和收信人使用的编码方式不一样。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码。

Unicode 当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母AinU+0041表示英语的大写字母AU+4E25表示汉字。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表

Unicode 的问题

Unicode虽然实现了编码的一统,但Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:

1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode。

2)Unicode 在很长一段时间内无法推广,直到互联网的出现。

UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。

这里的关系是,UTF-8 是 Unicode 的实现方式之一。

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

5.节点流,处理流

节点流:直接从数据源或目的地读写数据

处理流:不直接连接到数据源或目的地,而是“连接”在已存 在的流(节点流或处理流)之上,通过对数据的处理为程序提 供更为强大的读写功能。

常用流

InputStream/Reader:向外边读数据

OutpusStream/Writer:向外边写数据

节点流(文件流)

import java.io.*;

public class Main {

    public static void main(String args[]) throws IOException
    {
        /**
         * 读取文件
         * 1.建立一个流对象,将已存在的一个文件加载进流。
         *  FileReader fr = new FileReader(new File(“Test.txt”));
         * 2.创建一个临时存放数据的数组。
         *  char[] ch = new char[1024];
         * 3.调用流对象的读取方法将流中的数据读入到数组中。
         *  fr.read(ch);
         * 4. 关闭资源。
         *  fr.close();
         */

        FileReader fr = null;

            fr = new FileReader(new File("c:\\file.txt"));
            char[] buf = new char[1024];
            int len;
            while ((len = fr.read(buf)) != -1)
            {
                System.out.print(new String(buf, 0, len));
            }
            fr.close();


        /**
         * 写入文件
         * 1.创建流对象,建立数据存放文件
         *  FileWriter fw = new FileWriter(new File(“Test.txt”));
         * 2.调用流对象的写入方法,将数据写入流
         *  fw.write(“atguigu-songhongkang”);
         * 3.关闭流资源,并将流中的数据清空到文件中。
         *  fw.close();
         */

        FileWriter fw = new FileWriter(new File("d:\\filewriter.txt"));
            fw.write("9999999999999");

            fw.close();


        /**
         * 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文
         * 件将被覆盖。
         *  如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,
         * 在文件内容末尾追加内容。
         */

    }
}

缓冲流(一种处理流)

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类 时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

BufferedInputStream BufferedOutputStream

BufferedReader BufferedWriter

只要关闭最外层流即可,关闭最外层流也 会相应关闭内层节点流。

flush()方法的使用:手动将buffer中内容写入文件。

import java.io.*;

public class Main {

    public static void main(String args[]) throws IOException
    {


            BufferedReader br =  new BufferedReader(new FileReader("d:\\br.txt"));
            BufferedWriter bw =  new BufferedWriter(new FileWriter("d:\\bw.txt"));

            String str;
            while ((str = br.readLine()) != null) { // 一次读取字符文本文件的一行字符
                System.out.println(str);
                bw.write(str); // 一次写入一行字符串
                bw.newLine(); // 写入行分隔符
            }
            bw.flush(); // 刷新缓冲区

        br.close();
        bw.close();
    }
}

转换流

转换流提供了在字节流和字符流之间的转换

Java API提供了两个转换流:

InputStreamReader:将InputStream转换为Reader

OutputStreamWriter:将Writer转换为OutputStream

  • 字节流中的数据都是字符时,转成字符流操作更高效。
  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。
import java.io.*;

public class Main {

    public static void main(String args[]) throws IOException
    {
        /**
         * 将old.txt中的文件读出写入到new.txt里
         */


        FileInputStream fis = new FileInputStream("d:\\old.txt");
        FileOutputStream fos = new FileOutputStream("d:\\new.txt");

        InputStreamReader isr = new InputStreamReader(fis, "GBK");
        OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");

        BufferedReader br = new BufferedReader(isr);
        BufferedWriter bw = new BufferedWriter(osw);

        String str = null;
        while ((str = br.readLine()) != null) {
            bw.write(str);
            bw.newLine();
            bw.flush();
        }
        bw.close();
        br.close();
    }
}

参考资料:

字符编码笔记:ASCII,Unicode 和 UTF-8https://link.zhihu.com/?target=http%3A//www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券