Java I/O (Input/Output)——文件字节流

news2024/11/26 22:31:53
  • 博客主页:誓则盟约
  • 系列专栏:Java SE 专栏
  • 关注博主,后期持续更新系列文章
  • 如果有错误感谢请大家批评指出,及时修改
  • 感谢大家点赞👍收藏⭐评论✍ 

Java I/O 简介

        Java I/O(输入/输出)是 Java 程序中用于处理数据输入和输出的重要部分。

输入流(Input Streams):用于从数据源读取数据。常见的输入流包括FileInputStream(从文件读取)、BufferedInputStream(提高读取效率)等。

输出流(Output Streams):用于将数据写入到目的地。例如FileOutputStream(向文件写入)、BufferedOutputStream(提高写入效率)。

字符流(Reader 和 Writer):处理字符数据,更适合处理文本。如FileReaderFileWriter

缓冲流(Buffered Streams):通过缓冲区来减少实际的 I/O 操作次数,提高性能。

对象流(Object Streams):用于实现对象的序列化和反序列化,如ObjectInputStreamObjectOutputStream

        在实际编程中,根据具体的需求选择合适的 I/O 流可以提高程序的效率和可读性。


计算机总线结构:   

        那么为什么会有I/O呢?其实I/O无时无刻不在我们身边,比如读取硬盘上的文件,网络文件的传输,鼠标键盘输入,也可以是接受单片机发回的数据,而能够支持这些操作的设备就是I/O设备。

我们可以大致看一下整个计算机的总线结构

        最核心的是CPU,CPU像计算机的大脑一样,是计算机的核心部件,几乎所有的计算都是靠这个CPU来进行的,CPU懂的比较多,它可以对各种类型进行计算,但是随着时代的发展对图形的要求越来越高,CPU就略显乏力;于是就出现了GPU(显卡),显卡就是专门对于图形进行计算。

        通过北桥芯片连接到内存,这样CPU就可以对内存进行操作;南桥芯片是用于读取U盘或者硬盘内的数据 。

        常见的I/O设备一般是鼠标、键盘这类通过USB进行传输的外设或者是通过Sata接口或是M.2连接的硬盘。一般情况下,这些设备是由CPU发出指令通过南桥芯片间接进行控制,而不是由CPU直接操作。

        而我们在程序中,想要读取这些外部连接的!O设备中的内容,就需要将数据传输到内存中。而需要实现这样的操作,单单凭借一个小的程序是无法做到的,而操作系统(如:Windows/inux/MacOS)就是专门用于控制和管理计算机硬件和软件资源的软件,我们需要读取一个IO设备的内容时,就可以向操作系统发出请求,由操作系统帮助我们来和底层的硬件交互以完成我们的读取/写入请求。

        JDK提供了一套用于IO操作的框架,为了方便我们开发者使用,就定义了一个像水流一样,根据流的传输方向和读取单位,分为字节流InputStream和OutputStream以及字符流Reader和Writer的IO框架,当然,这里的流指的是数据流,通过流,我们就可以一直从流中读取数据,直到读取到尽头,或是不断向其中写入数据,直到我们写入完成,而这类IO就是我们所说的BIO。


文件字节流:

        字节流一次读取一个字节,也就是一个 byte 的大小,而字符流顾名思义,就是一次读取一个字符,也就是一个 char 的大小(在读取纯文本文件的时候更加适合)。

文件输入流:

在 Java 中,文件输入流FileInputStream用于从文件中读取数据。FileInputStream 允许程序以字节为单位读取文件的内容。

创建方式
通常通过指定要读取的文件路径来创建文件输入流对象。例如:

try {
    FileInputStream fis = new FileInputStream("your_file_path");
    // 后续的读取操作
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

        但是这种方式需要处理各种可能的异常,比如 FileNotFoundException 异常和 IOException 异常,并且需要手动关闭文件,完整代码如下:

public class Hello_World {
    public static void main(String[] args) { // 想读取一个文件  创建一个文件输入流   使用完把流关闭掉 释放掉 close
        FileInputStream stream = null;
        try {
            stream = new FileInputStream("绝对路径/相对路径");
//            stream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

        仅仅是取得文件就如此费劲,不合乎常理。所以有了try-with-resources 语句这种简便方式,try-with-resources 语句是一种用于更方便、更安全地管理资源(如输入流、输出流、数据库连接等)的机制。

优点

  1. 自动资源管理:无需显式地调用 close 方法来关闭资源,避免了因忘记关闭资源而导致的资源泄漏问题。
  2. 简洁的代码:减少了样板代码,使代码更简洁、更易读。

语法格式

try (Resource res = new Resource()) {
    // 使用资源的操作
} catch (Exception e) {
    // 异常处理
}

        对于以上示例的完整代码转成try-with-resources 语句如下:

public class Hello_World {
    public static void main(String[] args) {
        try(FileInputStream inputStream = new FileInputStream("路径")){ // 直接在try()中定义要在完成之后释放的资源
        } catch (IOException e){ // 这里变成IOException是因为调用close()可能会出现,而FileNotFoundException是继承自IOException的
            e.printStackTrace();
        }// 无需再编写finally语句块,因为在最后自动帮我们调用了close()。
    }
}

        由此可见,try-with-resources 语句极大地提高了资源管理的便利性和可靠性,使代码更加健壮和易于维护。

数据的传递:

        如图所示,在计算机数据由文件向内存进行传递的形式是以二进制01串进行的,一次一个字节,就像水流一样源源不断的传输,直至文件传输结束。

        数据不断传输过来,那我们如何去读取数据呢?

        调用read()方法是必要的,但是read()方法的调用方式也有很多种,这里主要列出来常见的三种。

1.直接读取

       try(FileInputStream inputStream = new FileInputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt")){ // 直接在try()中定义要在完成之后释放的资源
            int i = inputStream.read();
            System.out.println((char)i);
            int x = inputStream.read();  // 当没有内容后,会返回-1
            System.out.println((char)x);
      } catch (IOException e){ // 这里变成IOException是因为调用close()可能会出现,而FileNotFoundException是继承自IOException的
            e.printStackTrace();
        }// 无需再编写finally语句块,因为在最后自动帮我们调用了close()。
    

        由于读取数据返回的是int类型的一个数据,所以我们用int i 去接收它,然后利用强制类型转换把i 转为char类型进行输出。调用一次读取一个字符,当读取完之后会返回-1.这样效率较为低下,所以有下面第二种读取方法。

2.循环读取

         由于读取完之后会返回数字-1,所以可以利用这一性质进行while循环进行读取,直到返回-1时结束循环,代码如下:

try(FileInputStream inputStream = new FileInputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt")){ // 直接在try()中定义要在完成之后释放的资源
            int i;
            while ((i = inputStream.read()) != -1) {
                System.out.print((char)i);
            }
        } catch (IOException e){ // 这里变成IOException是因为调用close()可能会出现,而FileNotFoundException是继承自IOException的
            e.printStackTrace();
}// 无需再编写finally语句块,因为在最后自动帮我们调用了close()。
    

        通过这种方式就可以一次性对文件内的内容全部读取。但是由于不够灵活,可变性较差,所以还可以用下面第三种方法进行读取。

3.区间读取

        区间读取,顾名思义就是定义一个固定长度的区间,将文件内的内容按照这个区间大小进行读取,当文件未读内容小于区间长度时会以小于区间长度的形式进行最后一次读取,若没有元素可读取时,一样会返回-1。具体代码如下:

        try(FileInputStream inputStream = new FileInputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt")){ // 直接在try()中定义要在完成之后释放的资源

            System.out.println(inputStream.available()); // 获取有多少个数据可读
            byte [] bytes = new byte[inputStream.available()]; // 一次读x个数据
            while (inputStream.read(bytes) != -1)   // 当最后不足x个或者已经没有时,会返回少于x个的数据或者-1
                System.out.println(new String(bytes));

        } catch (IOException e){ // 这里变成IOException是因为调用close()可能会出现,而FileNotFoundException是继承自IOException的
            e.printStackTrace();
        }// 无需再编写finally语句块,因为在最后自动帮我们调用了close()。

        读取过程中可使用available()方法查询可读数量,在上面的案例中,我将区间长度x设置为了可读长度,这样也可以一次性读取完文件内数据。也可以设置其他int类型的x作为长度参数。

        这种方法在文件输出流常用,一个字节一个字节的读取出来并一个字节一个字节的写入另一个文件,相当于文件的拷贝操作。

       跳过操作:skip()方法。给skip(x)传人参数x,可以设置跳过前几个字节进行读取其下一个字节。


文件输出流:

        文件输出流(FileOutputStream)用于将数据写入到文件中。文件输出流允许您以字节为单位向文件写入数据。

        在写入之前您需要提供要写入的文件的路径和名称。如果文件不存在,它将被创建;如果文件已存在,默认情况下,新写入的数据会覆盖原有的内容。

   try {
       FileOutputStream fos = new FileOutputStream("your_file.txt");
   } catch (IOException e) {
       e.printStackTrace();
   }

  stream.flush()方法的主要作用是将输出流缓冲区中的数据强制刷新并输出。通常,当我们使用输出流(如 FileOutputStream 、BufferedOutputStream 等)写入数据时,数据并不是立即被发送到目的地(如文件),而是先被存储在缓冲区中。缓冲区的目的是减少实际的 I/O 操作次数,从而提高性能。

        然而,在某些情况下,我们希望确保数据能够立即被发送出去,而不是等到缓冲区填满或者输出流被关闭。这时就可以使用 flush 方法。  

        默认情况下(append的参数默认是false),写入的内容会直接取代原文件内的内容,即覆盖掉。代码如下:

public class Hello_World {
    public static void main(String[] args) {
        try(FileOutputStream stream = new FileOutputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt")){
            stream.write("Hello World".getBytes());  // 直接取代原内容
            stream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

        如果想接着文件的内容往后继续写(追加模式),那么只需要把append的参数改为true即可,代码如下:

public class Hello_World {
    public static void main(String[] args) {
        try(FileOutputStream stream = new FileOutputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt",true)){ // 加上true 变成追加模式
            stream.write("Hello World".getBytes());  // 直接取代原内容
            stream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

        至此,我们就完成了输出流操作,那么,就可以结合输入流和输出流进行拷贝操作了。

文件的拷贝:

        文件拷贝是将一个文件的内容完整地复制到另一个文件的操作。相关的类有:

   import java.io.FileInputStream;
   import java.io.FileOutputStream;
   import java.io.IOException;

        文件的拷贝操作一般使用读取数据的第三种方法,区间读取。因为这种方法可以设置足够大的区间,读取速度较快,不需要一个字节一个字节的去读取。下面是一个拷贝的示例代码:

public class Hello_World {
    public static void main(String[] args) {
        try(FileInputStream in = new FileInputStream("C:\\Users\\Xxy63\\Desktop\\无限弹窗代码.txt");
            FileOutputStream out = new FileOutputStream("C:\\Users\\Xxy63\\Desktop\\copy.txt")){
            byte[] bytes = new byte[1024];
            int len;
            while ((len = in.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            } // 拷贝速度大大提升
        }catch (IOException  e){
            e.printStackTrace();
        }
    }
}

        在上述代码中,通过创建输入流 FileInputStream 从源文件读取数据,创建输出流 FileOutputStream 向目标文件写入数据。使用一个缓冲区来提高拷贝效率,每次读取一定数量的字节到缓冲区,然后将缓冲区中的数据写入目标文件,直到读取完源文件的所有内容。

文件拷贝在很多场景中都很有用,比如:

  • 数据备份:将重要文件复制一份以防止数据丢失。
  • 共享文件:将文件拷贝到多个位置以便不同的程序或用户使用。

        例如,如果您有一个包含重要配置信息的文件,为了安全起见,可以定期进行备份拷贝。又或者在一个文件处理系统中,需要将原始文件拷贝到多个不同的目录下以供不同的模块处理。


 “且将新火试新茶,诗酒趁年华。”——《望江南·超然台作》

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

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

相关文章

抖音ip地址怎么换到别的地方

在数字化时代,抖音作为一款风靡全球的短视频社交平台,让我们的生活充满了无限乐趣与创意。然而,有时我们可能希望自己的抖音能够显示一个不同于当前所在地的IP地址,无论是出于隐私保护、还是其他个性化需求。那么,如何…

Linux学习记录(三)-----文件io和标准io的区别

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言文件IO和标准IO的区别1.\r和\n的区别2.缓冲2.1缓冲区的概念2.2.缓冲区的分类 3.文件IO和标准IO的区别 前言 文件IO和标准IO的区别 1.\r和\n的区别 \r 回车操作…

无人机之植保机篇

一、什么是植保无人机 植保无人机是用于农林植物保护作业的无人驾驶飞机,该型无人飞机由飞行平台、导航飞控、喷洒机构三部分组成,通过地面遥控器或导航飞控,来实现喷洒作业,可以喷洒药剂、种子、粉剂等。目前国内销售的植保无人机…

【已解决】VSCode连接Linux云服务器,代码写着写着服务器突然挂了是怎么回事?

文章目录 1. 问题描述2. 问题原因3. 解决方法 1. 问题描述 在使用 VSCode 连接远程 Ubuntu 云服务器写代码的时候,感觉越写越卡,代码提示半天出不来,最后更是直接断开连接了: 即使把 VSCode 关了,再重启也没用&#x…

五种IO模型与阻塞IO

个人主页:Lei宝啊 愿所有美好如期而遇 IO本质 我们常说IO就是input,output,也就是输入和输出,但是,他的本质是什么?站在OS角度,站在进程的角度,IO是什么?我们想&#…

申请专利需要准备哪些材料?

申请专利需要准备哪些材料?

代码之外的生存指南——自我营销

你是否有去过酒吧、夜店看过驻场乐队的演出? 你到了那里面听过之后你会发现那些乐队的演唱水平丝毫不亚于原唱的艺术家们,都很有才华; 你有没有想过【为什么这些驻场乐队就只能在那小小的夜店里做驻唱演出,每天疲于奔命&#xff0…

图综述-GGNN详解

A Survey of Geometric Graph Neural Networks:Data Structures, Models and Applications 本文主要介绍了在化学领域的分子设计和预测任务中,如何利用几何图神经网络(Geometric Graph Neural Networks,简称GGNN)来处理具有几何信…

怎样可以撰写出一篇优质软文呢?

现在这个互联网飞速发展的时代,软文推广已经逐渐变成了现在很多企业和品牌的推广宣传方式了,虽然软文推广操作起来很简单,但是想要做好没那么简单,软文稿件的质量和推广的流量更是息息相关。 好的软文不止可以让转化更高&#xff…

不容忽视!猫咪浮毛问题严重,宠物空气净化器教你一招清理

拥有一只可爱的猫咪,无疑是家庭中温馨与欢乐的源泉,但同时,宠物的毛发管理也成为了不少家庭面临的日常挑战。每当家中四处飘散着它们细腻的毛发,尤其是拥抱过后,衣物上不经意间沾满了轻盈的浮毛,宠物们随着…

【Material-UI】按钮组件的限制及解决方法详解

文章目录 一、ButtonBase 组件与禁用状态的限制1. 默认行为:pointer-events: none示例代码 2. 显示 not-allowed 光标的解决方案方法一:纯 CSS 解决方案方法二:DOM 结构变化 3. 工具提示(Tooltip)与禁用状态 二、其他常…

Javascript——原始数据类型的自动装箱

自动装箱 聊个例子介绍例子 聊个例子 ‘asd’.length //这其中是怎么执行的在 JavaScript 中,字符串是原始数据类型(primitive data type),但它们表现得像对象,因为它们有属性和方法。当你对一个字符串使用 .length 属…

原型图绘制技巧

针对于 Axure RP绘图软件。 1、拉辅助线 目的,确定画布大小尺寸从上面和左面的刻度尺上,点击鼠标,拖动,就可以拉出一条线。 2、画布底模设为组件 右键转换为母版,方便后续其他页面使用 3、按钮 按钮字体不要太大&am…

【Material-UI】Button 组件自定义详解

文章目录 一、自定义 Button 组件的基础1. 基于 styled 方法的自定义2. styled 方法详解3. 覆盖默认样式 二、高级自定义技巧1. 主题色彩与调色板2. 无障碍性与响应式设计 三、集成与测试1. 集成到项目中2. 测试自定义样式 四、总结 在使用 Material-UI 开发 Web 应用时&#x…

动态贝叶斯网络DBN介绍

动态贝叶斯网络DBN介绍 1. 引言2. 贝叶斯网络与动态贝叶斯网络2.1 贝叶斯网络简介2.2 动态贝叶斯网络详细介绍2.3 两种网络对比 3. 搭建动态贝叶斯网络的方法3.1 定义网络结构3.2 参数学习3.3 推理3.4 结构学习和参数学习的方法3.4.1 结构学习3.4.2 参数学习 4. 总结5. 参考文献…

【医学影像】无痛安装mamba

去年编辑的一个帖子。摆了一段时间后重新回归,发送一下作为状态分界线。 很癫狂的体验,man,what can i say! issue查看我的狗急跳墙状态 1.确定版本 cuda nvcc -Vpython python --versiontorch pip show torch2.下载对应版本…

电燃灶(电焰灶)、燃气灶、电磁炉,谁更契合中国人的烹饪习惯?

中国人的烹饪文化源远流长,丰富多样的烹饪方式和独特的口味需求造就了对炉灶的严格要求。在现代厨房中,电燃灶(电焰灶)、燃气灶和电磁炉成为了常见的选择。那么,究竟哪一种更适合中国人的烹饪习惯呢? 一、明…

[Paddle] 从零安装 Paddle 框架

1 安装前准备 1.1 安装环境 本机安装环境如下: 运行环境操作系统Ubuntu 22.04.4 LTS (Jammy Jellyfish)CPUIntel Core™ i5-12400显卡NVIDIA GeForce RTX 4090 1.2 验证 GPU 是否为 NVIDIA GPU 在命令行输入以下代码,以验证您的 GPU 是否是 NVIDIA …

零基础报考软考只是为了证书,应该报考初级还是中级呢?

证书的价值仍然非常吸引人~ 软考不仅是技术水平考核,也是评定职称资格的考试,是评定职称的主要条件。国有企业和事业单位的员工可以凭借软考中级以上的证书申请中级职称、副高级职称或者高级职称。此外,软考对于提升职位和加薪有着重要的作用…

嘉立创EDA专业版本创建自定义元件和封装

嘉立创EDA专业版本创建自定义元件和封装 1.新建封装2.新建元件1.新建封装 然后保存就可以了 2.新建元件 这里主要添加新建的封装