当前位置: 首页 > news >正文

Java IO精髓:高效块读写入技术深入解析

文件的复制

    首先我们先用昨天学习的知识来完成文件的复制,使用read和write方法来完成。

    public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("./image.png");FileOutputStream fos = new FileOutputStream("./copy_image.png");int d;int num = 0;long startTime = System.currentTimeMillis();
//        d = fis.read();
//        fos.write(d+1);哪怕改一个值图片都无法正常显示
//如果直接修改数据流中的任意字节(如你尝试的fos.write(d+1);),这很可能导致生成的图片文件损坏,
// 因为图片的格式(如PNG, JPEG等)有严格的字节排列和数据结构要求。
// 一个字节的改动可能会破坏图片的头部信息、颜色表、像素数据或其他关键部分。while ((d = fis.read()) != -1) {fos.write(d);num++;}long endTime=System.currentTimeMillis();System.out.println("复制完毕");System.out.println(num);System.out.println("复制时间为:"+(endTime-startTime)+"毫秒");fis.close();fos.close();}

在昨天的学习中,我们学习了如何向文件中写入或者从文件中读取数据,但是在面临需要读入或者写出大量数据时,例如上面我们要复制一张图片,或者是一首歌,又或者是一个视频时,我们需要不断的用循环来调用read和write方法?很明显这样做很浪费性能,并且上述案例在复制文件时的读写效率是很低的。因为硬盘的特性,决定着硬盘的读写效率差,而单字节读 写就是在频繁调用硬盘的读写,从而产生了"木桶效应"。 为了解决这个问题,我们可以采取使用块读写的方式来复制文件,减少硬盘的实际读写的次数,从而提 高效率。

 

 块读入

InputStream定义了块读方法                                                        

int read(byte[ ] data)

一次性读取给定字节数组data总长度的字节量并存入到该数组中。 返回值表达实际读取到了多少字节。如果返回值为-1表达流读取到末尾了

 下面我将用拆分来详细解释块读入是如何运行的

            原文件内容:11001100 01010101 10101010 00111100 11001111 00110011 11101100--------------------------------------------------------------byte[] data = new byte[3];//长度为3的字节数组data:{00000000,00000000,00000000}int len=0;第一次调用:len = fis.read(data);//因为data长度为3,因此一次性读取3字节并存入该数组原文件内容:11001100 01010101 10101010 00111100 11001111 00110011 11101100^^^^^^^^ ^^^^^^^^ ^^^^^^^^连续读取3个字节data:{11001100,01010101,10101010}读取后数组内容就是读取到的内容len:3 len接收的返回值为整数,表示本次实际读取到了3个字节-----------------------------------------------------------------第二次调用:len = fis.read(data);//因为data长度为3,因此一次性读取3字节并存入该数组原文件内容:11001100 01010101 10101010 00111100 11001111 00110011 11101100^^^^^^^^ ^^^^^^^^ ^^^^^^^^连续读取3个字节data:{00111100,11001111,00110011}读取后数组内容就是读取到的内容len:3   len接收的返回值为整数,表示本次实际读取到了3个字节----------------------------------------------------------------第三次调用:len = fis.read(data);//因为data长度为3,因此一次性读取3字节并存入该数组原文件内容:11001100 01010101 10101010 00111100 11001111 00110011 11101100^^^^^^^^ ^^^^^^^^ ^^^^^^^^应当读取3个,实际读取到1个连续读取3个字节data:{11101100,11001111,00110011}本次读取  |--上次的旧数据---|len:1   len接收的返回值为整数,表示本次实际读取到了1个字节-----------------------------------------------------------------第四次调用:len = fis.read(data);//因为data长度为3,因此一次性读取3字节并存入该数组原文件内容:11001100 01010101 10101010 00111100 11001111 00110011 11101100^^^^^^^^ ^^^^^^^^ ^^^^^^^^本次没有数据可读(EOF)data:{11101100,11001111,00110011}|------上次的旧数据--------|len:-1   已经是文件末尾了

块写出

OutputStream中定义了块写操作

void write(byte[ ] data)

一次性将给定字节数组data中的所有字节写出

 小问题

 在学习完上面的两个方法后相信你已经学会如何把这张图片去复制了,但是如果你去实践后,就会发现速度问题解决了,但是复制后的文件比原文件大一些。这是文件不一定是10240的倍数,这会导致最后 一次读取时是读不够10240的字节数的,那么data数组中就不是所有数据都是新数据了。此时如果在写 出时将data数组所有内容写出就会出现文件最后多出很多旧的数据。

我们用一张图片来解释

我们每次都是读入四个字节的数据,但是在复制到第四次也就是最后一次时只剩下紫色部分的数据,而显然这不能填满data数组的长度,因此再最后写入复制文件时后面的数据还是蓝色的也就是上次复制时剩下的旧数据。

解决 

使用OutputStream的另一个块写操作

void write(byte[] data,int offset,int len)

将给定数组data从offset处开始的连续len个字节一次性写

 我们来看看最后的解决

        FileInputStream fis = new FileInputStream("./image.png");FileOutputStream fos = new FileOutputStream("./copy_image02.png");byte[] data = new byte[1024 * 100];int len = 0;long start=System.currentTimeMillis();while ((len = fis.read(data)) != -1) {fos.write(data,0,len);}long end=System.currentTimeMillis();System.out.println("耗时:"+(end-start)+"毫秒");fos.close();fis.close();

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Beyond Compare忽略特定格式文本
  • 我写的全部R包和函数,持续更新中
  • 微信小游戏授权问题
  • Shader 透明相关
  • RK3568-设备树休眠唤醒
  • 【ACM出版,快录用】2024年智能医疗与可穿戴智能设备国际学术会议(SHWID 2024, 10月18-20)
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • leetcode 80 删除有序数组中的重复项 II
  • 24.8.26学习心得
  • Python将Word文档转为PDF
  • go国内源设置
  • 四,接口类型和网络协议
  • 数据结构-全部由1组成的子矩形数量
  • Springboot统一给redis缓存的Key加前缀
  • 又一个坑爹:未启用约束一行或多行中包含违反非空、唯一或外键约束的值。
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 时间复杂度分析经典问题——最大子序列和
  • Bootstrap JS插件Alert源码分析
  • Docker下部署自己的LNMP工作环境
  • Facebook AccountKit 接入的坑点
  • Laravel5.4 Queues队列学习
  • magento 货币换算
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • PHP面试之三:MySQL数据库
  • 缓存与缓冲
  • 检测对象或数组
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 判断客户端类型,Android,iOS,PC
  • 如何进阶一名有竞争力的程序员?
  • 时间复杂度与空间复杂度分析
  • 使用parted解决大于2T的磁盘分区
  • 项目管理碎碎念系列之一:干系人管理
  • 在Unity中实现一个简单的消息管理器
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #pragma data_seg 共享数据区(转)
  • #window11设置系统变量#
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • %check_box% in rails :coditions={:has_many , :through}
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (160)时序收敛--->(10)时序收敛十
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (笔记)M1使用hombrew安装qemu
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (六)vue-router+UI组件库
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (十六)一篇文章学会Java的常用API
  • (转)Sql Server 保留几位小数的两种做法
  • (转)用.Net的File控件上传文件的解决方案
  • *2 echo、printf、mkdir命令的应用