【Java】IO流:字节流 字符流 缓冲流

news2025/1/11 7:00:39

接续上文,在这篇文章将继续介绍在Java中关于文件操作的一些内容

【Java】文件操作


文章目录

  • 一、“流”的概念
    • 1.“流”的分类
      • 1.1输入流和输出流
      • 1.2字节流和字符流
    • ==字节和字符的区别?==
    • ==为什么要有字符流?==
      • 1.3节点流和处理流
    • ==字符流自带缓冲区,为什么还要用字符缓冲流?==
    • 2.“流”的特性
    • 3.“流”的分类
  • 二、Stream流(字节流)
    • 1.InputStream流
      • 版本一(无参)read()
      • 版本二 read(byte[] b)
      • 版本三 read(byte[] b,int offset,int len)
    • 2.OutPutStream流
  • 三、Stream流(字符流)
    • reader
    • writer
  • 四、 scanner

一、“流”的概念

“流”是一个抽象的概念,是对输入输出设备的一种抽象理解,在Java中,对数据的输入输出都是以“流”的的方式进行的。“流”具有方向性,输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流,相反,写出数据到某个数据源目的地的时候也会开启一个输出流。数据源可以是文件、内存或者网络等
在这里插入图片描述

1.“流”的分类

“流”序列中的数据可以是未经加工的原始二进制数据,也可以是经过一定编码处理后符合某种格式的特定数据,因此java中的“流”分为以下三种流:

  1. 按数据流的方向:输入流、输出流
  2. 按处理数据单位:字节流、字符流
  3. 按功能:节点流,处理流

1.1输入流和输出流

“流”具有方向性,输入流、输出流是相对的
输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反。
在这里插入图片描述

1.2字节流和字符流

字节流:数据流中的最小的数据单元是字节,一次读入读出8位二进制;
字符流:数据流中的最小的数据单元是字符,一次读入读出16位二进制,java中的字符是Unicode编码,一个字符占用两个字节。

字节和字符的区别?

  • 存储方式:

字节(byte):字节是计算机存储和通信的基本单位。在Java中,一个字节由8位(bit)组成,可以表示256种不同的状态,其取值范围是-128到127(对于byte类型)。
字符(char):字符用于表示文本信息。在Java中,一个字符使用Unicode编码,占用2个字节(16位)的空间。因此,Java中的char类型可以表示65536种不同的字符。

  • 表示范围:

由于字节只有8位,其表示范围相对较小,只能表示-128到127之间的整数,或者0到255之间的无符号整数。
字符类型则能表示更多的字符,包括各种文字、符号等。由于使用了Unicode编码,Java中的char类型可以表示世界上几乎所有的字符。

为什么要有字符流?

在UTF8 编码中,“爱吃南瓜的北瓜”对应的字节如下
在这里插入图片描述
如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。

  • 用途:

字节主要用于处理二进制数据、图像、音频、视频等非文本信息,或者在网络通信中传输数据。
字符则主要用于处理文本信息,如字符串、文件名、用户输入等。
在Java中,经常需要将字节和字符进行转换。例如,当我们从文件或网络读取数据时,通常得到的是字节流,而我们需要将其转换为字符流以便进行文本处理。这时,可以使用Java提供的解码器(Decoder)将字节转换为字符。反之,当需要将文本信息写入文件或发送到网络时,需要将字符转换为字节,这时可以使用编码器(Encoder)。

  • 区别
  1. 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
  2. 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。

1.3节点流和处理流

  • 节点流:节点流可以从一个特定的数据源读写数据,如FileInputStream ,FileOutputStream ,FileReader ,FileWrite

在这里插入图片描述

  • 处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)
    在这里插入图片描述

在这里插入图片描述

在诸多处理流中,有一个非常重要,那就是缓冲流。

我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。

在这里插入图片描述

联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。、

在这里插入图片描述

需要注意的是,缓冲流效率一定高吗?不一定,某些情形下,缓冲流效率反而更低

字符流自带缓冲区,为什么还要用字符缓冲流?

尽管字符流已经具备了缓冲的功能,但字符缓冲流(BufferedReader 和 BufferedWriter)仍然有其自身的优势和用途:

缓冲区大小可控:字符缓冲流提供了更大的缓冲区,可以指定缓冲区的大小。较大的缓冲区可以一次性读取或写入更多的字符数据,减少对底层I/O的频繁访问,提高读写效率。

提供了更方便的读写方法:字符缓冲流提供了一些便捷的方法,如 readLine() 方法可以一次读取一行数据,而不需要一个字符一个字符地读取。newLine() 方法可以写入一个平台特定的换行符,而不需要手动处理不同操作系统的换行符。

支持预读取和回滚:字符缓冲流具有 mark() 和 reset() 方法,可以在读取过程中进行标记(mark)并在需要时回滚(reset),方便进行预读取和回溯操作。

支持写入自动刷新:字符缓冲流提供了 flush() 方法,用于手动刷新缓冲区,并将缓冲区中的数据强制写入底层的输出流。此外,可以通过设置缓冲区的大小和自动刷新策略(如自动换行符)来控制写入时的刷新。

2.“流”的特性

一般来说关于流的特性有下面几点:

  1. 先进先出:最先写入输出流的数据最先被输入流读取到。
  2. 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
  3. 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

3.“流”的分类

“流”存在于Java.io包中,主要包含四种基本的类,InputStream、OutputStream、Reader及Writer类,它们分别处理字节流和字符流:

输入\输出字节流字符流
输入InputStreamReader
输出OutputStreamWriter

二、Stream流(字节流)

1.InputStream流

在这里插入图片描述

修饰符及返回值类型⽅法签名说明
intread()读取⼀个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b中,返回实际读到的数量;-1 代表以及读完了
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

在这里插入图片描述
这里InputStream是一个抽象类,不能实例化
因为它不仅仅对应的是硬盘的文件,也可以对应网卡,也可以对应控制台,也可以对应蓝牙设备
这里我们是对文件进行操作,所以我们使用系统提供对应的api来进行实例
这里使用FileInputStream

签名说明
FileInputStream(File file)利⽤ File 构造⽂件输⼊流
FileInputStream(String name)利⽤⽂件路径构造⽂件输⼊流

版本一(无参)read()

版本一(无参类型)读取的是字节类型,那么为什么返回值的类型却是int呢???

我们点开read()的源码

在这里插入图片描述
手动翻译一下

从输入流中读取下一个字节的数据。返回值byte为int型,取值范围为0到255。如果由于到达流的末尾而没有可用的字节,则返回值-1。此方法将一直阻塞,直到输入数据可用、检测到流的结尾或抛出异常。子类必须提供此方法的实现。返回:数据的下一个字节,如果到达流的结尾则返回-1。抛出:IOException -如果I/O错误发生。

此处返回int
1)为了有额外的余地来表示“到达末尾”-1这样的情况
2)确保读到的数据都是正数
   原则上来说,字节这样的概念,本应该是无符号的
   但是byte类型,本身是有符号的。
   此处使用int就可以确保读出来的字节都是正数,按照“无符号”来处理了.
3)为什么不用short?
  因为short是两个字节,int是四个字节
  随着计算机技术的发展,空间的存储成本会越来越低的,
  CPU的不断发展,每次读取的数据也会越来越长
  对于32位的CPU来说,一次读取四个字节的数据
  也就是说在计算机内部,如果是short也会转换成int来处理
  对于64位的CPU来说,一次就会读取八个字节的数据
  所以short的使用会逐步的减少,int来代替,
 public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("D:/hello.txt")){
             while (true){
                int b = inputStream.read();

                 if (b == -1){
                     break;
                 }
                 System.out.printf("0x%x ",b);
             }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

版本二 read(byte[] b)

    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("D:/hello.txt")){
            byte[] bytes = new byte[1024];

            int len;
            while (true){
                len = inputStream.read(bytes);
                if (len == -1){
                    break;
                }
                for (int i = 0; i < len; i++) {
                    System.out.printf("0x%x ", bytes[i]);
                }
            }
        }  catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

这两种版本的访问速度谁更快呢?

版本二速度快,
访问硬盘是低效操作,IO次数越多,整体速度就越慢.

版本三 read(byte[] b,int offset,int len)

一次读取一部分,放置到指定位置

在网络协议中,一个报文就是由 报头 和 载荷 组成的
我们就可以用版本三来读取

2.OutPutStream流

修饰符及返回值类型⽅法签名说明
voidwrite(int b)写⼊要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写⼊ os 中
intwrite(byte[] b, int off, int len)将 b 这个字符数组中从 off 开始的数据写⼊ os 中,⼀共写 len 个
voidclose()关闭字节流
voidflush()重要:我们知道 I/O 的速度是很慢的,所以,⼤多的 OutputStream为了减少设备操作的次数,在写数据的时候都会将数据先暂时写⼊内存的⼀个指定区域⾥,直到该区域满了或者其他指定条件时才真正将数据写⼊设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操作,将数据刷到设备中。

OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使⽤ FileOutputStream

同read一样,也是三个版本
在这里插入图片描述

 public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("D:/hello.txt",true)) {
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
            outputStream.write(101);
            outputStream.write(102);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

这里的参数中多了一个Boolean类型
这个是打开了"追加写"的意思
这个功能是在对文件内容进行写入时,不会清空上次文件中的内存
会在文件的原有内容中继续写入,
在这里插入图片描述

为什么要将InputStream OutputStream流的创建写入到try() {} catch 里的小括号内呢?

这是因为我们在创建流后,在流使用后,需要对流进行关闭,在写入到try后面的小括号内时,我们就可以不必手动的来进行流的关闭,减少一些情况下我们忘记关闭流这一操作带来的风险,
这一语法被称作 try–with–resources。
但并非所有的类都可以放到try()的括号里,这个类必须实现Closeable接口
我们点开InputStream的源码
在这里插入图片描述
可以看到,InputStream流实现了closeable接口。

三、Stream流(字符流)

reader

在这里插入图片描述
点开read()的源码
在这里插入图片描述
手动翻译一下

读取单个字符。此方法将阻塞,直到字符可用、发生I/O错误或到达流的末尾。想要支持有效的单字符输入的子类应该覆盖这个方法。返回:读取的字符,取值范围为0到65535 (0x00-0xffff)的整数,如果到达流的末尾则为-1。抛出:IOException -如果发生I/O错误

取值范围为0到65535 (0x00-0xffff)也就是读取两个字节

public static void main(String[] args) {
        try(Reader reader = new FileReader("D:/hello.txt")){
               while (true){
                   int c = reader.read();

                   if (c == -1){
                       break;
                   }
                   char ch = (char)c;
                   System.out.printf("%c",ch);
               }
        }catch (IOException e){
            e.printStackTrace();
        }

在这里插入图片描述

那么不出意外的话,意外就要来了
Java中的char是两个字节,而汉字确实三个字节
但是read()确可以正常读取,这是为什么呢?

在文件中的原始数据,是三个字节一个字符
read在读取操作时,能够识别文件时UTF8编码方式
读的是三个字节,返回成一个char的时候,把UTF8编码方式,转换成了Unicode,在Unicode中一个汉字是两个字节的
链接: Java中一个汉字究竟占几个字节?

在Java内部,
char用的是Unicode
String里面默认是UTF8

那么有小可爱就可能会提出疑问了
那直接用char来接收read的返回值不就可以了,还可以省去转换这一步

这样做肯定有这样做的道理
char只能表示两个字节
文件末尾的-1怎么表示呢?

writer

在这里插入图片描述
writer也存在“追加写”
在这里插入图片描述

四、 scanner

Scanner scanner = new Scanner(System.in);

你是否好奇Sacnner里的System.in到底是什么
我们点开in的源码
在这里插入图片描述


以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞

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

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

相关文章

基士得耶(GESTETNER ) CP 6303C 速印机简介

规格参数 产品名称: 基士得耶&#xff08;GESTETNER &#xff09; CP 6303C 速印机 品牌中文: 基士得耶/GESTETNER 型 号: CP-6303C 工作方式&#xff1a; 数码式 制版方式: 自动印刷 制版时间&#xff1a; 曝光玻璃: 31秒(A4长边…

使用SpringBoot+Redis做一个排行榜【推荐】

SpringBoot Redis实现排行榜 一、Zset有序集合介绍 Zset是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分( score) ,这个评分( score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的&#xff0c;但是评分可以是重复了…

MATLAB 基于规则格网的点云抽稀方法(自定义实现)(65)

MATLAB 基于规则格网的点云抽稀方法(自定义实现)(65) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 海量点云的处理,需要提前进行抽稀预处理,相比MATLAB预先给出的抽稀方法,这里提供一种基于规则格网的自定义抽稀方法,步骤清晰,便于理解抽稀内涵, 主要涉及到使…

【深度学习】网络安全,SQL注入识别,SQL注入检测,基于深度学习的sql注入语句识别,数据集,代码

文章目录 一、 什么是sql注入二、 sql注入的例子三、 深度学习模型3.1. SQL注入识别任务3.2. 使用全连接神经网络来做分类3.3. 使用bert来做sql语句分类 四、 深度学习模型的算法推理和部署五、代码获取 一、 什么是sql注入 SQL注入是一种常见的网络安全漏洞&#xff0c;它允许…

CSS-盒子模型元素溢出

作用&#xff1a;控制溢出的元素的内容的显示方式 属性&#xff1a;overflow 属性值 属性值效果hidden溢出隐藏scroll溢出滚动&#xff08;无论是否溢出&#xff0c;都显示滚动条位置&#xff09;auto溢出滚动&#xff08;溢出才显示滚动条位置&#xff09; <!DOCTYPE html&…

字体设计_西文字体设计(英文字体设计)

一 西文字体设计基础知识 设计目标和历史成因 设计目标&#xff1a;让眼睛看着舒服的字体 那什么样的字体让眼睛看着舒服呢&#xff1f; 让眼睛看着舒服的字体造型其实是我们记忆里的手写体、自然造型。 所以就能理解西文字体为什么同一笔画&#xff0c;有的地方粗有的地方…

国科大深度学习期末历年试卷

本文借鉴 国科大深度学习复习 深度学习期末 深度学习2020 一&#xff0e;名词解释&#xff08;每个2分&#xff0c;共10分&#xff09; 深度学习&#xff0c;稀疏自编码器&#xff0c;正则化&#xff0c;集成学习&#xff0c;Dropout 二&#xff0e;简答题&#xff08;每题…

【汇总】虚拟机网络不通(Xshell无法连接虚拟机)排查方法

搜索关键字关键字关键字&#xff1a;虚拟机虚拟机虚拟机连接失败、虚拟机无法连接、Xshell连接失败、ping baidu.com失败、静态IP设置 Kali、CentOS、远程连接 描述&#xff1a;物理机无法连接虚拟机&#xff1b;虚拟机无法访问百度&#xff0c;虚拟机无法访问baidu.com 虚拟机…

五月加仓比特币

作者&#xff1a;Arthur Hayes Co-Founder of 100x. 编译&#xff1a;Liam 编者注&#xff1a;本文略有删减 (以下内容仅代表作者个人观点&#xff0c;不应作为投资决策的依据&#xff0c;也不应被视为参与投资交易的建议或意见&#xff09;。 从四月中旬到现在&#xff0c;当你…

5月8日学习记录

_[FBCTF2019]RCEService&#xff08;preg_match函数的绕过&#xff09; 涉及知识点&#xff1a;preg_match函数绕过&#xff0c;json的格式&#xff0c;正则回溯 打开环境&#xff0c;要求用json的格式输入 搜索学习一下json的语法规则 数组&#xff08;Array&#xff09;用方括…

抓取Google时被屏蔽怎么办?如何避免?

在当今数字化时代&#xff0c;数据采集和网络爬取已成为许多企业和个人必不可少的业务活动。对于爬取搜索引擎数据&#xff0c;特别是Google&#xff0c;使用代理IP是常见的手段。然而&#xff0c;使用代理抓取Google并不是一件轻松的事情&#xff0c;有许多常见的误区可能会导…

VisualGLM-6B微调(V100)

Visualglm-6b-CSDN博客文章浏览阅读1.3k次。【官方教程】XrayGLM微调实践&#xff0c;&#xff08;加强后的GPT-3.5&#xff09;能力媲美4.0&#xff0c;无次数限制。_visualglm-6bhttps://blog.csdn.net/u012193416/article/details/131074962?ops_request_misc%257B%2522req…

一键自动化博客发布工具,用过的人都说好(阿里云篇)

阿里云有个开发者社区&#xff0c;入驻过的朋友可能想要把自己的博客发布到阿里云社区上。 今天我来介绍一下blog-auto-publishing-tools自动发布博客到阿里云的实现原理。 阿里云的博客发布界面比较简单&#xff0c;只有标题&#xff0c;正文&#xff0c;摘要&#xff0c;关…

【已解决】ModuleNotFoundError: No module named ‘IPython‘

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《AI实战中的各种bug…

城市向导系统源码线下陪玩APP小程序源码陪玩软件开发运营

项目背景 随着后疫情时代的到来&#xff0c;当下旅游市场可以说是相当火爆&#xff0c;但是很多游客却抛弃旅行社&#xff0c;都跑到陪玩平台上面去找搭子找陪玩&#xff0c;选择符合自己的出行方式&#xff0c;尤其是年轻人&#xff0c;更在乎别具一格的旅行路线和体验。那么…

鸿蒙开发接口Ability框架:【@ohos.ability.particleAbility (particleAbility模块)】

particleAbility模块 particleAbility模块提供了Service类型Ability的能力&#xff0c;包括启动、停止指定的particleAbility&#xff0c;获取dataAbilityHelper&#xff0c;连接、断开当前Ability与指定ServiceAbility等。 说明&#xff1a; 本模块首批接口从API version 7开…

【分治算法】【Python实现】线性时间选择

文章目录 [toc]问题描述随机选择算法Python实现时间复杂性 BFPRT算法时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;分治算法 学习指南&#xff1a;Python学习指南 问题描述 给定线性序集中 n n n个元素和一个整数 k ( 1 ≤ k ≤ n ) k (1 \leq k \…

华为数据之道第一部分导读

目录 导读 第一部分 序 第1章 数据驱动的企业数字化转型 非数字原生企业的数字化转型挑战 业态特征&#xff1a;产业链条长、多业态并存 运营环境&#xff1a;数据交互和共享风险高 IT建设过程&#xff1a;数据复杂、历史包袱重 数据质量&#xff1a;数据可信和一致化…

ICME2024 | 基于半监督对比学习的表现力语音合成

人类的语音极富表现力&#xff0c;不仅包括语调和重读&#xff0c;还包括风格和情感等多种元素。表现力语音合成的目标是要精准捕捉并再现这些元素。先前表现力语音合成方面的研究通常将表现力视为单一维度&#xff0c;如风格或情感。但实际上&#xff0c;风格可以随着文本和场…

【SDN:逻辑上集中的控制平面,路由选择算法,LS路由工作过程,距离矢量路由选择(distance vector routing)】

文章目录 SDN&#xff1a;逻辑上集中的控制平面SDN的主要思路SDN控制平面和数据平面分离的优势SDN 架构: 数据平面交换机 路由选择算法路由(route)的概念最优化原则(optimality principle)路由的原则路由算法的分类LS路由工作过程&#xff08;相当于一个上帝&#xff09;链路状…