张小飞的Java之路——第四十二章——字节流

news2024/9/29 15:24:29

写在前面:

视频是什么东西,有看文档精彩吗?

视频是什么东西,有看文档速度快吗?

视频是什么东西,有看文档效率高吗?


诸小亮:下面我们学习——字节流

张小飞:什么是字节流?

诸小亮:就是以字节的形式操作文件中的数据,分为:InputStream、OutputStream

  • Stream:流,InputStream是输入流,OutputStream是输出流

张小飞:这个。。。,还是不太懂,能否演示一下?

FileOutputStream

诸小亮:好吧,我们先看——FileOutputStream

FileOutputStream:文件输出流,是 OutputStream 的子类,用于保存数据到文件

张小飞:您的意思是,可以利用 FileOutputStream 把数据存到文件中?

诸小亮:是的

张小飞:那么,具体怎么操作呢?

创建对象

诸小亮:首先,需要创建 FileOutputStream 对象,有两种方式?

张小飞:是给它一个 文件路径 吗?

诸小亮:不错,比如:


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

    //1. 创建输出流对象,如果指定路径上的文件不存在,会自动创建,如果存在会自动覆盖
    FileOutputStream out = new FileOutputStream("G:\\learn\\hero.txt");
}

张小飞:我这里怎么报错了呢?

image.png
诸小亮:这是因为你提供的路径上的文件夹不存在,所以抛出 FileNotFoundException

张小飞:哦~~,原来如此

诸小亮:除了根据 文件路径 创建对象之外,还可以根据 File 创建,比如:

FileOutputStream out = new FileOutputStream(new File("G:\\learn\\hero.txt"));

张小飞:这种方式,如果路径上的文件夹不存在,也会报错吗?

诸小亮:是的

存储数据

张小飞:那么,如何保存数据呢?

诸小亮:使用它的 write 方法写入字节数据,比如:

public static void main(String[] args) throws IOException {
    //1. 创建输出流对象
    FileOutputStream out = new FileOutputStream("G:\\learn\\hero.txt");

    //2. 调用写入功能
    String str = "yase,lvbu,liubei";
    //write:接收一个字节数组,把数组中的所有数据都写到输出流中
    out.write(str.getBytes());

    //3. 流是系统资源,需要释放,理论上应该放到finally中
    out.close();
}

诸小亮:运行程序后,你就会发现硬盘上会多一个 hero.txt 文件

张小飞:还真是的,好神奇

诸小亮:接下来,我们优化一下代码

public static void main(String[] args) {
    FileOutputStream out = null;
    try{
        //FileOutputStream 可能报异常,放到try中
        out = new FileOutputStream("G:\\learn\\hero.txt");
        String str = "yase,lvbu,liubei";
        out.write(str.getBytes());
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        //创建 out 对象可能失败,所以要判断是不是 null;
        if(out!=null){
            try {
                out.close();
            } catch (IOException e) {
                //如果close失败,我们无法处理,直接抛出
                throw new RuntimeException("流关闭失败:"+e.getMessage());
                //注意:本人没见过close方法失败的,但还是得这样写
            }
        }
    }
}

张小飞:您这是优化代码吗?

诸小亮:是的,还记得我们说过嘛,流——是系统资源,使用过后要关闭

张小飞:记得,记得,不过您上面的代码也太麻烦了吧

诸小亮:这倒是,不过还可以再次优化

public static void main(String[] args) {
    //JDK7 开始,可以把 out 的定义放到 try 中,会自动 close
    try(FileOutputStream out = new FileOutputStream("G:\\learn\\hero.txt")){
        String str = "yase,lvbu,liubei";
        out.write(str.getBytes());
    }catch (Exception e){
        e.printStackTrace();
    }
}

张小飞:嗯,这样子就优雅多了

续写和换行

张小飞:上面的程序,每次执行程序后,发现文件中的内容都一样,不能往文件中追加内容吗?

诸小亮:目前上面代码,每次执行都会重新创建文件,所以内容都一样,当然可以追加内容,比如:

public static void main(String[] args) throws IOException {
    //1. 第二个参数是 append:true表示追加数据,false表示重新创建文件
    FileOutputStream out = new FileOutputStream("G:\\learn\\hero.txt", true);

    //2. 调用写入功能
    String str = "diaochan,change,xishi";
    //write:接收一个字节数组,把数组中的所有数据都写到输出流中
    out.write(str.getBytes());

    //3. liu是系统资源,需要释放,应该放到finally中
    out.close();
}

张小飞:嗯嗯,不错,再次执行后,内容追加成功了

image.png

张小飞:还有个问题,能不能写入数据的时候,换一行?

诸小亮:这个也没问题,加上 \r\n 就可以实现换行,比如:

image.png

结果:image.png

张小飞:明白了,我也来试试

诸小亮:不过,建议使用 System.lineSeparator()

张小飞:嗯?这是为什么呢?

诸小亮:\r\n 是 window 系统中的换行符,也就是说,当程序跑在 window 中才可以换行

如果是 linux 、Mac 就不行了,使用 System.lineSeparator(),可以让程序跨平台

image.png

结果:

image.png
张小飞:明白了

FileInputStream

诸小亮:接着是——FileInputStream

FileInputStream:文件输出流,是 InputStream 的子类,用于读取数据到内存中

张小飞:原来使用它读取文件数据

创建对象

诸小亮:FileInputStream创建对象的方式跟 FileOutputStream 一行,不再详细解释了

public static void main(String[] args) throws FileNotFoundException {
    //第一种:根据路径创建FileInputStream
    FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
    //第二种:根据File创建FileInputStream
//        FileInputStream in = new FileInputStream(new File("G:\\learn\\hero.txt"));
}

如果路径上的文件夹都不存在,则报错,比如:

image.png

结果:

image.png

读取数据

张小飞:它是如何读取数据的呢?

诸小亮:它读取的方式有好几种,我们一个个介绍

单字节读取

诸小亮:第一种,单字节读取

张小飞:这是什么意思?

诸小亮:它提供了一个 read 方法,但是每次只能读取一个字节,比如:

FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
//读取数据,每次只是读取一个字节,当 返回 -1 时,文件读取完毕
int read = in.read();
System.out.println(read);
//关闭流
in.close();

结果:image.png

张小飞:呀,结果怎么是一个数字?

诸小亮:多新鲜啊,字节是 byte ,这不是数字类型吗?

张小飞:噢~,差点儿忘记了,那为什么是 121 呢?

诸小亮:文件中第一个字母是 y,而 121 就是字母 y 对应的ASCII

张小飞:明白了,再把它转换为 char 类型,就行了吧

诸小亮:是的,把 int 转换为 char

image.png

结果:image.png

一次性读取

张小飞:这一次读取一个字节也太费劲了,有没有其他方式?

诸小亮:readAllBytes 可以一次读出所有内容,返回字节数组,比如:

FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
//readAllBytes:一次读出所有内容
byte[] bytes = in.readAllBytes();
//把字节数组转换问String
String content = new String(bytes);
System.out.println(content);
//关闭流
in.close();

结果:

image.png

张小飞:嗯嗯,这个好,以后就用这个吧

读取指定大小

诸小亮:一点儿都不好,如果文件太大,这样读取的话会占用很多内存,导致程序卡死的

张小飞:。。。。。,那怎么办?

诸小亮:它提供的 read 有重载的方法,可以一次读取指定大小

  • read(byte b[]):一次读取指定数量的字节,放到数组中,并返回读取的字节数量

张小飞:原来还有这种方法,怎么不早点说出来呢?

诸小亮:这不是让你也了解一下其他方法嘛

public static void main(String[] args) throws IOException {
    FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
    //指定一次读取 16 字节
    byte[] buffer = new byte[16];
    //read方法读取的内容都会放到buffer中,当返回 -1 时候,文件读取完毕
    int read = in.read(buffer);
    //把字节数组转换为String
    String content = new String(buffer);
    System.out.println(content);
    //关闭流
    in.close();
}

结果:image.png

张小飞:这才读取了 16 字节,得把文件中的内容都读出来啊

诸小亮:使用循环就行了

public static void main(String[] args) throws IOException {
    FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
    //指定一次读取16字节
    byte[] buffer = new byte[16];
    //read方法读取的内容都会放到buffer中,当返回-1时候,文件读取完毕
    int read = 0;
    while((read = in.read(buffer)) != -1){
        //把字节数组转换问String
        String content = new String(buffer);
        System.out.print(content);
    }
    //关闭流
    in.close();
}

结果:

image.png

张小飞:您这个输出,怎么多出来一些内容?

诸小亮:因为最后一次读出来的数据不够16字节,数组中还存留着上一次读出的内容,所以。。。。

张小飞:那,这样肯定不行啊,怎么解决?

诸小亮:放心,放心,read的返回值是每次读出来的字节数,可以用用它的返回值

image.png

诸小亮:上图,每次读出来几个字节就转换几个字节

结果:

image.png

张小飞:嗯嗯,这样好多了

缓冲区大小的设置

诸小亮:你知道我们使用缓冲区的目的是什么吗?

张小飞:不就是为了一次多读出一些数据吗?

诸小亮:其实准确来说,是减少读取文件的次数

张小飞:嗯?为什么?

诸小亮:因为读写文件是IO操作,需要操作硬盘,频繁的读写性能会很差

张小飞:那,目前缓冲区是 16 字节,如果一个文件太大,不是还会读很多次吗?

诸小亮:所以我们要合理设置缓冲区的大小

张小飞:怎么合理设置呢?

诸小亮:有个 available 方法 :返回流中剩余的未读的字节数量(其实就是文件大小)

张小飞:然后呢?

诸小亮:然后就可以根据我们的需求设置缓冲区了,比如:

image.png
按照上图中设置,最多读 6 次就可以读完

张小飞:那如果文件很大呢?比如 5 个 G

诸小亮:。。。。。,那这就要看你机器性能了,性能很高,就可以设置比较大的缓冲区

张小飞:哦,好吧

诸小亮:另外,还有一个获取文件大小的方法——length

image.png

复制文件

诸小亮:我们已经学习了 FileInputStream 、FileOutputStream,你能用它们做一个文件复制功能吗?

张小飞:这个。。。。,还不太行

诸小亮:好吧,先给你一个示例

public static void main(String[] args) throws IOException {
    //从源文件中读
    FileInputStream in = new FileInputStream("G:\\learn\\hero.txt");
    //目标文件
    FileOutputStream out = new FileOutputStream("G:\\hero.txt");
    //指定一次读取16字节
    byte[] buffer = new byte[16];
    int read = 0;
    while((read = in.read(buffer)) != -1){
        //每次只是写入这一次读取的字节数量
        out.write(buffer,0,read);
    }
    //关闭流
    in.close();
    out.close();
}

结果:

image.png

诸小亮:其中原理图都给你画好了

image.png

BufferedInputStream 和 BufferedOutputStream

诸小亮:接下来我们看看——BufferedInputStream、BufferedOutputStream

张小飞:这是做什么的?

诸小亮:它们是——字节流缓冲区对象,目的是增强 InputStream 和 OutputStream 的功能

张小飞:如何增强?

诸小亮:BufferedInputStreamBufferedOutputStream 内部有一个字节数组

张小飞:缓冲区?

诸小亮:是的,使用它们后,读写顺序是这样的

  • 先从源文件读数据到 **BufferedInputStream **的内部数组
  • 然后从 **BufferedInputStream **中再读到自定义的数组
  • 接着从自己定义的数组写到 BufferedOutputStream 的内部数组
  • 最后写到硬盘的新文件中

image.png

代码:

public static void main(String[] args) throws IOException {
    BufferedInputStream inBuffer = new BufferedInputStream(new FileInputStream("G:\\learn\\hero.txt"));
    BufferedOutputStream outBuffer = new BufferedOutputStream(new FileOutputStream("G:\\hero.txt"));
    byte[] buffer = new byte[16];
    int read = 0;
    //之前是从硬盘中读取,现在是从 BufferedInputStream 中读取
    while((read = inBuffer.read(buffer)) != -1){
        //之前直接写到硬盘上,现在写到 BufferedOutputStream 中
        outBuffer.write(buffer,0,read);
    }
    //关闭流
    inBuffer.close();
    inBuffer.close();
}

张小飞:是不是写错了,我运行了程序,发现文件中没有任何内容啊

诸小亮:这是因为 BufferedOutputStream 中的数组大小是 8192 字节,而我们文件 83 字节

  • 默认只有 BufferedOutputStream 中的数组满了后才会写到硬盘上

张小飞:那该怎么办?

诸小亮:可以手动调用 flush 方法,把 BufferedOutputStream 中的数据刷到硬盘上

image.png

张小飞:好的,我再试试

中文数据

张小飞:刚刚读写的都是英文,能不能弄点儿中文?

诸小亮:当然可以了

public static void main(String[] args) throws IOException {
    FileOutputStream out = new FileOutputStream("G:\\learn\\hero.txt");
    String str = "亚瑟";
    out.write(str.getBytes());
    out.close();
}

张小飞:我看了一下文件大小,默认用的是 UTF-8 编码吗?

image.png

诸小亮:用的什么编码,得看你自己在 idea 中怎么配置的

image.png

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/586175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

article-并联机械手爪运动学分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aNKIR4E-1685371700448)(data:image/svgxml;utf8, )] 2.4.3 基于Robotics Toolbox的工具箱的模型检测 上文中,我们已经对采摘机器手爪运动学理论模型进行了创建,接下来要用MA…

V3.0_用exec族函数替代system()

注意点:exec族函数的使用 以execl为例: (1) (2)exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。 执行成功后不会返回,也不会从源程序调用点接…

Redis缓存一致性问题及解决方案

对于没有并发的用户请求 先更新缓存,后更新数据库先更新数据库,后更新缓存 两者第二步没成功,都有问题 如果更新缓存成功,更新数据库没成功,一旦缓存失效,读取的仍是旧值如果更新数据库成功,…

MySQL官网下载Linux版本安装包

步骤一:https://www.mysql.com/ 步骤二:https://www.mysql.com/downloads/ 步骤三:https://dev.mysql.com/downloads/ 步骤四:https://dev.mysql.com/downloads/mysql/

二十分钟秒懂:实现前后端分离开发(vue+element+spring boot+mybatis+MySQL)

目录 开发者介绍 什么是前后端分离开发 vue与springboot开发的优势 Vue.js 的优势: Spring Boot 的优势: vue与springboot如何实现前后端连接 demo简介 重要部分前端部分代码 重要部分后端代码 后端解决跨域问题 Controller部分 xml部分 se…

商城APP开发需要哪些功能,如何选择开发公司

商城APP开发的功能有很多,从功能上看主要分为以下几个大类: 一、商品展示类:商家可以在这里展示自己产品的种类、数量、价格等信息,消费者在浏览和选择的时候,能直观的看到商家介绍。 二、优惠促销类:商家…

JDBC与DBCP整合

DBCP:DataBase Connection Pool,数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接…

第四章 相似矩阵与矩阵对角化

引言 题型总结中推荐例题有蓝皮书的题型较为重要,只有吉米多维奇的题型次之。码字不易,如果这篇文章对您有帮助的话,希望您能点赞、评论、收藏,投币、转发、关注。您的鼓励就是我前进的动力! 知识点思维导图 补充&…

【Web服务器集群】Apache配置与应用

文章目录 一、构建虚拟web主机1.概述2.httpd服务支持的虚拟主机类型3.构建虚拟Web主机3.1基于域名的虚拟主机3.2基于IP地址的虚拟主机3.3基于端口的虚拟主机 4.Apache连接保持5.Apache访问控制 二、Apache日志管理rotatelogs分隔工具 三、总结1.Web虚拟主机部署步骤2.网页根目录…

DN-DETR代码学习笔记

代码地址:GitHub - IDEA-Research/DN-DETR: [CVPR 2022 Oral]Official implementation of DN-DETR 论文地址: https://arxiv.org/pdf/2203.01305.pdf DN-DETR是在DAB-DETR的基础上完成的,DN-DETR的作者认为导致DETR类模型收敛慢的原因在于匈牙…

【Linux升级之路】4_进程控制

文章目录 前言一、【Linux初阶】fork进程创建 & 进程终止 & 进程等待二、【Linux初阶】进程程序替换 | 初识、原理、函数、应用 & makefile工具的多文件编译三、【Linux初阶】进程替换的应用 - 简易命令行解释器的实现结语 前言 本片博客是 Linux操作系统 进程控制…

【周末闲谈】谈谈数学转码这一年来的体会与反思

——我们走了太远,以至于忘了为何出发 前言 笔者本科读的是数学专业,就是每天和数学分析、高等代数、概率论、随机过程等等这些理论打交道的专业,这个专业出来工作好像一般有两个方向就是金融和计算机,我选择了计算机方向。主要…

【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版)

ChatGLM-6B模型结构代码解析(单机版) ​ 本文介绍ChatGLM-6B的模型结构,代码来自https://huggingface.co/THUDM/chatglm-6b/blob/main/modeling_chatglm.py。 相关博客 【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版) 【自然语言处理】【大模型】BL…

日撸 Java 三百行day56-57

文章目录 day56-57 kMeans 聚类1.kMeans聚类理解2.代码理解2.1代码中变量的理解2.2代码理解 day56-57 kMeans 聚类 1.kMeans聚类理解 无监督的机器学习算法,其中k是划分为几个簇,并且选择k个数据作为不同簇的聚类中心,计算每个数据样本和聚…

ASRT语音识别系统的部署以及模型的使用(运用篇)

ASRT语音识别系统的部署以及模型的使用(运用篇) 前言 ASRT是一个中文语音识别系统,由AI柠檬博主开源在GitHub上。 GitHub地址:ASRT_SpeechRecognition 国内Gitee镜像地址:ASRT_SpeechRecognition 文档地址:ASRT语音识别工具文…

Python打包成EXE

一、使用Pyinstaller pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller 1.2Pyinstaller打包步骤 Pyinstaller -F -w -i apple.ico py_word.py 结果: 运行结果: 二、使用Auto-py-to-exe auto-py-to-exe 是一个用于打包 python 程序…

第二届(2023年)中国国际培育钻石产业发展与创新大会盛大召开!

5月25-26日,由广东省商务厅、中国国际贸易促进委员会广东省委员会(广东国际商会)、广州市商务局、番禺区人民政府、广东省交易控股集团有限公司/广东省公共资源交易中心指导,广州钻石交易中心(简称广钻中心&#xff09…

C语言深度解析--指针

目录 指针 指针的定义: 指针的大小: 指针和指针类型 野指针 指针运算 指针-整数: 指针-指针: 指针的关系运算: 指针和数组 二级指针 指针数组 理解指针的第一步是在机器级上观察指针表示的内容。大多数现代…

第十六届全国大学生信息安全竞赛创新实践赛初赛部分WP AGCTF战队

持续两天的比赛,打的很累,web没有出太多的题,比赛被pwn师傅带飞了,希望下此加油,下边是此次比赛排名。 文章目录 MISC签到卡被加密的生产流量国粹调查问卷pyshell CRYPTO基于国密SM2算法的密钥密文分发可信度量Sign_i…

Java中的深拷贝和浅拷贝介绍

文章目录 基本类型和引用类型Clone方法浅拷贝深拷贝小结 在讲解什么是深拷贝和浅拷贝之前,我们先来了解一下什么是基本类型和引用类型。 基本类型和引用类型 基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、…