04 字节流的使用


一、字节输入流:每次读取一个字节

IO体系结构图

文件字节输入流:FileInputStream

作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

构造器 说明
public FileInputStream(File file) 创建字节输入流管道与源文件对象接通
public FileInputStream(String pathname) 创建字节输入流管道与源文件路径接通

?? 

方法名称 说明
public int read() 每次读取一个字节返回, 如果字节已经没有可读的返回-1
public int read(byte[] buffer) 每次读取一个字节数组返回, 如果字节已经没有可读的返回-1

【代码示例】

public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
       // 1、创建一个文件字节输入流管道与源文件接通。
        // InputStream is = new FileInputStream(new File("file-io-app\\src\\data.txt"));
        // 简化写法
        InputStream is = new FileInputStream("file-io-app\\src\\data.txt");

        // 2、读取一个字节返回 (每次读取一滴水)
//        int b1 = is.read();
//        System.out.println((char)b1);   
//
//        int b2 = is.read();
//        System.out.println((char)b2);
//
//        int b3 = is.read();
//        System.out.println((char)b3);
//
//        int b4 = is.read(); // 读取完毕返回-1
//        System.out.println(b4);

        // 3、使用循环改进
        // 定义一个变量记录每次读取的字节    a  b  3   爱 因为“爱”三个字节,会出现乱码
        //                              o o  o   [ooo]
        int b;
        while (( b = is.read() ) != -1){
            System.out.print((char) b);
        }
    }
}

二、字节输入流:每次读取一个字节数组

文件字节输入流:FileInputStream

作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

方法名称 说明
public int read() 每次读取一个字节返回, 如果字节已经没有可读的返回-1
public int read(byte[] buffer) 每次读取一个字节数组返回, 如果字节已经没有可读的返回-1

每次读取一个字节数组存在什么问题?

读取的性能得到了提升

读取中文字符输出无法避免乱码问题

【代码示例】

/**
   目标:使用文件字节输入流每次读取一个字节数组的数据。
 */
public class FileInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app/src/data02.txt");

        // 2、定义一个字节数组,用于读取字节数组
//        byte[] buffer = new byte[3]; // 3B
//        int len = is.read(buffer);
//        System.out.println("读取了几个字节:" + len);
//        String rs = new String(buffer);
//        System.out.println(rs);
//
//        int len1 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len1);
//        String rs1 = new String(buffer);
//        System.out.println(rs1);
//        // buffer = [a b c]
//
//        // buffer = [a b c]  ==>  [c d c]
//        int len2 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len2);
//        // 读取多少倒出多少
//        String rs2 = new String(buffer,0 ,len2);
//        System.out.println(rs2);
//
//        int len3 = is.read(buffer);
//        System.out.println(len3); // 读取完毕返回-1

        // 3、改进使用循环,每次读取一个字节数组
        byte[] buffer = new byte[3];
        int len; // 记录每次读取的字节数。
        while ((len = is.read(buffer)) != -1) {
            // 读取多少倒出多少
            System.out.print(new String(buffer, 0 , len));
        }
    }
}

三、字节输入流:一次读完全部字节

问题:

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

定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。

2、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?

如果文件过大,字节数组可能引起内存溢出

 方式以:

  ● 自己定义一个字节数组与文件的大小一样大, 然后使用读取字节数组的方法, 一次性读取完成。 

方法名称 说明
public int read(byte[] buffer) 每次读取一个字节数组返回, 如果字节已经没有可读的返回-1

方式二:

● 官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中 

方法名称 说明
public byte[] readAllBytes() throws IOException 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返

 【代码示例】

/**
   目标:使用文件字节输入流一次读完文件的全部字节。可以解决乱码问题。
 */
public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        File f = new File("file-io-app/src/data03.txt");
        InputStream is = new FileInputStream(f);

        // 2、定义一个字节数组与文件的大小刚刚一样大。
//        byte[] buffer = new byte[(int) f.length()];
//        int len = is.read(buffer);
//        System.out.println("读取了多少个字节:" + len);
//        System.out.println("文件大小:" + f.length());
//        System.out.println(new String(buffer));

        // 读取全部字节数组
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));

    }
}

四、字节输出流:写字节数据到文件

文件字节输出流:FileOutStream

作用: 以内存为基准, 把内存中的数据以字节的形式写出到磁盘文件中去的流。 

构造器 说明
public FileOutputStream?(File file) 创建字节输出流管道与源文件对象接通
public FileOutputStream?(File fileboolean append) 创建字节输出流管道与源文件对象接通, 可追加数据
public FileOutputStream?(String filepath) 创建字节输出流管道与源文件路径接通
public FileOutputStream?(String filepathboolean append) 创建字节输出流管道与源文件路径接通, 可追加数据

 文件字节输出流( FileOutputStream) 写数据出去的API 

方法名称 说明
public void write(int a) 写一个字节出去
public void write(byte[] buffer) 写一个字节数组出去
public void write(byte[] buffer , int pos , int len) 写一个字节数组的一部分出去。

 流的关闭与刷新 

方法 说明
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

【代码示例】

public class OutputStreamDemo04 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt" , true); // 追加数据管道
//        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt"); // 先清空之前的数据,写新数据进入

        // 2、写数据出去
        // a.public void write(int a):写一个字节出去
        os.write('a');
        os.write(98);
        os.write("\r\n".getBytes()); // 换行
        // os.write('徐'); // [ooo]

        // b.public void write(byte[] buffer):写一个字节数组出去。
        byte[] buffer = {'a' , 97, 98, 99};
        os.write(buffer);
        os.write("\r\n".getBytes()); // 换行

        byte[] buffer2 = "我是中国人".getBytes();
//        byte[] buffer2 = "我是中国人".getBytes("GBK");
        os.write(buffer2);
        os.write("\r\n".getBytes()); // 换行


        // c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
        byte[] buffer3 = {'a',97, 98, 99};
        os.write(buffer3, 0 , 3);
        os.write("\r\n".getBytes()); // 换行

        // os.flush(); // 写数据必须,刷新数据 可以继续使用流
        os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用了
    }
}

五、文件拷贝

拷贝样图:

案例:文件拷贝

需求:

● 把某个视频复制到其他目录下的“b.avi”

思路:

① 根据数据源创建字节输入流对象

② 根据目的地创建字节输出流对象

③ 读写数据,复制视频

④ 释放资源

【代码示例】

/**
 *   目标:学会使用字节流完成文件的复制(支持一切文件类型的复制)
 */
public class CopyDemo05 {
    public static void main(String[] args) {
        try {
            // 1、创建一个字节输入流管道与原视频接通
            InputStream is = new FileInputStream("file-io-app/src/out04.txt");

            // 2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

            // 4、关闭流。
            os.close();
            is.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}