【每日一问】使用IO流进行文件复制

    字节流    字符流
输入流 InputStream  Reader read()
输出流 OutputStream Writer write()

对上面名词的解释
字节流:一次读1个字节,1个字节=8个二进制位
字符流:一次读1个字符,1个字符=2个字节=一个char类型字符
‘a ‘、’中’、’?’

列举你知道的流
FileInputStream/FileOutputStream
FileReader/FileWriter
BufferedInputStream/BufferedOutputStream
BufferedReader/BufferedWriter
InputStreamReader/OutputStreamWriter
PrintWriter……

BufferedReader读一行的数据:String readLine();
PrintWriter写一行的数据:println(String);

使用IO流进行文件复制

public static void main(String[] args) {
        //1、确定源文件的路径
        String srcFilePath = "D:/abc.txt";
        //2、确定目标文件路径
        String destFilePath = "D:/abc-copy.txt";
        //3、创建读取源文件的FileInputStream
        FileInputStream fis = null;
        //4、创建写入目标文件的FileOutputStream
        FileOutputStream fos = null;

        try {
            //5、创建流的对象
            fis = new FileInputStream(srcFilePath);
            fos = new FileOutputStream(destFilePath);
            //6、循环读取,并且边读边写
            int b;//每次读取1个字符,可以自动转换为int
            while((b = fis.read())!=-1){
                fos.write(b);
                //补充:代码写完并没有真正写进去
                //必须调用flush或close才可以真正写入
                fos.flush();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    }

这种方式可以复制任何类型的文件,字节流只能处理ASCII(可以简单理解为键盘直接敲出来的,不依赖任何输入法的)

而字符流是不靠谱的,不能用字符流复制文件,字符流可以处理带中文或非ascii的文件

举个例子

‘中’字是1个字符,对应2个字节,也就是16个二进制位,比如0100 1110 0010 1101
字节流一次读1个字节,所以它把中这个字拆开来读了

现在我们复制一个大一点的文件,在刚才的代码上加上

long start = System.currentTimeMillis();
//1、确定源文件的路径
String srcFilePath = "D:/yasea-master.zip";
//2、确定目标文件路径
String destFilePath = "D:/yasea-master-copy.zip";
......
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

运行程序

开始文件大小对比,在程序运行中,被copy文件一直是0kb
这里写图片描述

运行结束时,输出日志

耗时:54667

直到flush之后才有大小

这里写图片描述

可以看到1个9M的文件复制了50多秒,效率很低,我们来进行改进

改进一

改为用BufferedInputStream和BufferedOutputStream

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //1、确定源文件的路径
        String srcFilePath = "D:/yasea-master.zip";
        //2、确定目标文件路径
        String destFilePath = "D:/yasea-master-copy.zip";
        //3、创建读取源文件的FileInputStream
        FileInputStream fis = null;
        //4、创建写入目标文件的FileOutputStream
        FileOutputStream fos = null;

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //5、创建流的对象
            fis = new FileInputStream(srcFilePath);
            fos = new FileOutputStream(destFilePath);

            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //6、循环读取,并且边读边写
            int b;//每次读取1个字符,可以自动转换为int
            while((b = bis.read())!=-1){
                bos.write(b);
                //补充:代码写完并没有真正写进去
                //必须调用flush或close才可以真正写入
                bos.flush();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                }
            }

            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));

    }

复制完成,输出日志

耗时:28082

改进二

我们将数据读入缓冲区

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //1、确定源文件的路径
        String srcFilePath = "D:/yasea-master.zip";
        //2、确定目标文件路径
        String destFilePath = "D:/yasea-master-copy.zip";
        //3、创建读取源文件的FileInputStream
        FileInputStream fis = null;
        //4、创建写入目标文件的FileOutputStream
        FileOutputStream fos = null;

        try {
            //5、创建流的对象
            fis = new FileInputStream(srcFilePath);
            fos = new FileOutputStream(destFilePath);
            //6、循环读取,并且边读边写
            /*int b;//每次读取1个字符,可以自动转换为int
            while((b = fis.read())!=-1){
                fos.write(b);
                //补充:代码写完并没有真正写进去
                //必须调用flush或close才可以真正写入
                fos.flush();
            }*/

            //读的时候会读入缓冲区的数组
            byte[] b = new byte[1024];
            int length;
            while((length = fis.read(b))!=-1){
                //写的时候直接写缓冲区的数组
                fos.write(b);
            }


        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally{
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));

    }

}

运行完成,查看日志

耗时:79

这种做法是牺牲空间换取时间,是划算的

关于缓冲区,有2点要说
1、文件越大,给的缓冲区越大
2、缓冲区大到一定程度,效果就不明显,推荐1024*8

刚才的程序可能有bug

例如一个文件2000byte,我们的缓冲区设为1024
第一次读取的时候读了1024,第二次只能读976,但是我们write的时候还是1024,所以文件大小可能会不对

这里写图片描述

所以最后一点要改进的就是

            //读的时候会读入缓冲区的数组
            byte[] b = new byte[1024];
            int length;
            while((length = fis.read(b))!=-1){
                //写的时候直接写缓冲区的数组
                fos.write(b,0,length);
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值