【JavaEE精炼宝库】文件操作(2)——文件内容读写 | IO流

文章目录

  • 一、输入流
    • 1.1 InputStream 概述:
    • 1.2 read 方法详解:
    • 1.3 close 方法:
    • 1.4 利用 Scanner 进行读操作:
    • 1.5 Reader:
  • 二、输出流
    • 2.1 OutputStream 概述:
    • 2.2 write 方法详解:
    • 2.3 利用 PrintWriter 进行写操作:
  • 三、小程序练习
    • 3.1 程序1:
    • 3.2 程序2:
    • 3.3 程序3:

在上一篇文章主要讲述了 Java 针对文件系统进行操作(创建文件,删除文件,创建目录,重命名文件)。本文章是上一篇文章的续集,主要讲述 Java 针对文件内容进行操作(读文件,写文件)。

读写操作就涉及到流的概念,Java 中通过 ‘流’(stream)这样的一组类,进行上述的文件内容操作。
在这里插入图片描述

主要分为输入流(InputStream)和输出流(OutputStream)这两类。

一、输入流

说明:InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用 FileInputStream。

1.1 InputStream 概述:

  • 方法:
修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了。
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量。-1 代表已经读完了。
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量。-1 代表已经读完了。
voidclose()关闭字节流。
  • 构造方法:
签名说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流

1.2 read 方法详解:

不难看到 read 方法有 3 种使用的版本,接下来我会分别给大家演示前两种(第 3 种就是第 2 种加上起点),并进行讲解。
在这里插入图片描述

  • 版本一:无参数,一次读取一个字节。读取到的内容,就通过返回值来进行表示。
    在这里插入图片描述

​​注意:这里的返回值是 int 类型,这是为什么呢?既然一次读取一个字节,返回值不是 byte 就行了?

答:注意有以下三个原因:

  1. 为了能够有额外的空间表示 “到达末尾” -1 这样的情况。
  2. 确保读取到的数据都是正数:从原则上来说,“字节” 这样的概念,本身就是 “无符号”,但是 byte 是带有符号的,使用 int 就可以确保读取出来的字节都是正数。按照 “无符号”的方式来处理了。
  3. 为什么不使用 short,而是使用 int?答:虽然使用 int 较 short 是浪费了 2 个字节。但是还是不建议使用 short 类型,这个类型随着硬件的升级,正在逐渐被取代。对于 32 位 CPU,一次处理 4 个字节的数据,使用 short,操作系统内部还是要把 short 转化成 int 来处理,32 位 CPU 如此,对于 64 位 CPU,short 就更加没有意义了。

案例演示:
关于为什么 inputStream 的创建为什么能为什么要写在try()里面,在后面会有详细解释,这里主要看 read 的使用。

import java.io.*;
public class demo2 {public static void main(String[] args) throws IOException {try(InputStream inputStream = new FileInputStream("./text.txt")){//推荐这样的写法。while(true){int n = inputStream.read();if(n == -1) break;System.out.printf("0x%x ",n);//按照十六进制打印}}}
}

案例演示结果:
在这里插入图片描述

  • 版本二:带有一个参数 byte[]。
    在这里插入图片描述

版本二和版本一的返回值的含义不太一样,具体差别如下:版本二的返回值代表读入 byte 数组的长度,如果返回 -1 表示读取完毕。 这个写法在 Java 中不太常见,这是一种属于 C++ 式的写法。用参数来作为函数的返回结果(输出型参数)。

一次读取多少,取决于数组的长度和文件内容多少,read 会尽可能把数组填满。

案例演示:

import java.io.*;
public class demo3 {public static void main(String[] args) throws IOException {try(InputStream inputStream = new FileInputStream("./text.txt")){byte[] bytes = new byte[1024];int n = 0;while(true){n = inputStream.read(bytes);if(n == -1) break;for(int i = 0;i < n;i++){System.out.printf("0x%x ",bytes[i]);}}}}
}

案例演示结果:
将文件完全读完的两种方式。相比较而言,后一种的 IO 次数更少,性能更好。
在这里插入图片描述

1.3 close 方法:

关闭文件是一个非常关键的操作。

为什么要关闭文件?

答:没关闭文件大部分情况下我们是感知不到的,但是一旦出现问题,就是大事(能击碎年终奖😭)。在打开文件的时候,会在操作系统内核,PCB 结构体(进程)中给“文件描述符表(长度存在上限,并且不能自动扩容)”添加一个元素,这个元素就是表示当前打开文件的相关信息。一旦占满了之后,再尝试打开,就会打开文件失败。(其他的操作,网络通信相关的操作就可能收到影响)。

inputStream.close();

上面的这种写法是一般的关闭文件的方式。但是这样写不太好,因为如果代码前面出现异常或者 return,此时 close 就执行不到了。虽然使用 try finally 可以解决问题,但是写出来的代码不太优化

try(){........
}finally{inputStream.close();
}

对于老程序猿来说代码是语文,要追求优雅。于是 Java 实现了把流对象的创建写到 try()里,代码执行出了 try{}时,就会自动调用 inputStreamclose 了。

写法如下:

try(InputStream inputStream = new FileInputStream("./text.txt"))

要抛异常(这里没写)。

注意:务必要实现 Closeable 接口的类,才能够放到 try() 里。
在这里插入图片描述

1.4 利用 Scanner 进行读操作:

上述栗子中,我们看到了对字符类型直接使用 InputStream 进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该共作,就是 Scanner 类。

构造方法说明
Scanner(InputStream is)构造一个新的 Scanner ,产生从指定输入流扫描的值。
Scanner(InputStream is, String charset)构造一个新的 Scanner ,产生从指定输入流扫描的值。使用 charset 字符集进行 is 的扫描读取。

示例如下:

public static void main(String[] args) throws IOException {try(InputStream inputStream = new FileInputStream("./text.txt")){try(Scanner in = new Scanner(inputStream)){}}}

1.5 Reader:

以字符为单位读取数据,在 InputStream 中,虽然可以通过 Scanner 传入字符集来达到读取字符的效果,但是这种办法并不是通用的。我们更推荐使用 Reader 来实现读取字符。
由于 Reader 的使用方法和 InputStream 类的使用方式基本一致,所以这里只叙述 read 方法。

在这里插入图片描述
注意:这里传入的是 char 类型的数组,这样就有足够的内存来保存字符了。

由于写法基本一样,这里就演示版本二(带字符数组)。

import java.io.*;
public class demo2 {public static void main(String[] args) throws IOException {try(Reader reader = new FileReader("./text.txt")){char[] chars = new char[1024];while(true){int n = reader.read(chars);if(n == -1) break;for(int i = 0;i < n;i++){System.out.print(chars[i]);}}}}
}

演示结果如下:
在这里插入图片描述

二、输出流

说明:OutputStream 只是一个抽象类,要使用还需要具体的实现类。关于 OutputStream 的实现类有很多,基本可以认为不同的输出设备都可以对应一个 OutputStream 类,我们现在只关心写文件,所以使用 FileOutputStream。

2.1 OutputStream 概述:

  • 方法:
修饰符及返回值类型方法签名说明
voidwrite(int b)将指定的字节写入此输出流。
voidwrite(byte[] b)b.length字节从指定的字节数组写入此输出流。
intwrite(byte[] b,int off,int len)从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
voidclose()关闭此输出流并释放与此流相关联的任何系统资源。
voidflush()刷新此输出流并强制任何缓冲的输出字节被写出。

解释一下 flush:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用flush(刷新)操作,将数据刷到设备中。

  • 构造方法:
构造方法说明
FileOutputStream(String name)创建文件输出流,以指定的名称写入文件
FileOutputStream(String name,boolean append)创建文件输出流,以指定的名称是否追加写入文件。

注意:在使用 FileOutputStream 创建指定文件对象的时候,如果不在文件名后面加上 true,原有的文件内容会被清空,如果加上 true 就是在原文件内容上追加写。

案例如下:
在这里插入图片描述
执行完try(OutputStream outputStream = new FileOutputStream("./text.txt"))后变成:
在这里插入图片描述

2.2 write 方法详解:

在这里插入图片描述
和 read 一样有三种版本,并且没有返回值,这里就只演示版本二(带byte 数组)。

  • 版本二:一次 write 若干字节。会把参数数组里所有的字节都写入文件中。

代码案例如下:

public class demo5 {public static void main(String[] args) throws IOException {try(OutputStream outputStream = new FileOutputStream("./text.txt")){byte[] bytes = new byte[4];for(int i = 0;i < 4;i++){bytes[i] = (byte)(97 + i);//对应是字符 abcd}outputStream.write(bytes);outputStream.flush();//不要忘记,flush 刷新缓存}}
}

案例演示结果:
在这里插入图片描述

2.3 利用 PrintWriter 进行写操作:

注意:OutputStream 也有 close 操作,这里没有单独写,是因为使用和注意点都一样。

上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用 PrintWriter 类来完成输出,因为 PrintWriter 类中提供了我们熟悉的 print/println/printf 方法。

案例演示如下:

public class demo6 {public static void main(String[] args) throws IOException {try(OutputStream outputStream = new FileOutputStream("./text.txt")){try(PrintWriter printWriter = new PrintWriter(outputStream)){printWriter.print("abcd");printWriter.flush();}}}
}

案例演示结果:
在这里插入图片描述

还有一个对应的字符写类 Writer 使用的方法是一样的,就不再赘述了。

三、小程序练习

我们学会了文件的基本操作 + 文件内容读写操作,接下来,我们实现一些小工具程序,来锻炼我们的能力。

3.1 程序1:

  • 程序要求:扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件。

步骤如下:

  1. 提示用户输入
  2. 找到名称包含指定字符的所有普通文件
  3. 是否删除

源码如下(有详细注释):

import java.io.*;
import java.util.*;//扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否
//要删除该⽂件
public class demo1 {static Scanner in = new Scanner(System.in);public static void main(String[] args) {//1提示用户输入System.out.println("请输入要查找的文件目录:");String dirName = in.next();List<File> result = new ArrayList<>();//2找到名称包含指定字符的所有普通文件File file = new File(dirName);//判断输入是否合法if(!file.isDirectory()){System.out.println("输入的目录不存在");return;}System.out.println("请输入要查找的指定字符:");String searchWord = in.next();searchFile(file,searchWord,result);System.out.println("一共找到了" + result.size() + "种结果");for(int i = 0;i < result.size();i++){System.out.println((i + 1) + "号:" + result.get(i).getAbsolutePath());}List<Integer> list = new ArrayList<>();//存储用来删除的下标System.out.println("请输入要删除第几号文件(输入0停止删除):");int y = in.nextInt();while(y != 0){y--;if(!result.get(y).exists()){System.out.println("已经被删除过来");}else{result.get(y).delete();System.out.println("删除成功");}y = in.nextInt();}}//查找目录底下的所有文件public static void searchFile(File file,String searchWord,List<File> result){if(file == null) return;//递归出口File[] files = file.listFiles();//返回目录底下的文件for(int i = 0;i < files.length;i++){if(files[i].isDirectory()){//目录的情况searchFile(files[i],searchWord,result);}else{//走到这里说明是普通文件//找文件名字是否包含关键字if(searchWord(files[i],searchWord)){//包含关键字的情况result.add(files[i]);}}}}//查找文件名是否包含关键字private static boolean searchWord(File file, String searchWord) {String fileName = file.getName();return fileName.contains(searchWord);}
}

程序运行结果如下:可以在电脑上单独创建一个文件夹来测试。
在这里插入图片描述

3.2 程序2:

  • 程序要求:进行普通文件的复制。

步骤如下:

  1. 提示用户输入
  2. 检查输入是否合法
  3. 进行复制

源码如下:

import java.util.*;
import java.io.*;//进⾏普通⽂件的复制
public class demo1 {public static void main(String[] args) throws IOException {Scanner in = new Scanner(System.in);//1.提示读入System.out.println("请输入要复制的文件");String srcFileRoot = in.next();//检查输入是否合法File srcFile = new File(srcFileRoot);if (!srcFile.isFile()) {System.out.println("输入的不是普通文件路径");return;}//已经是文件了System.out.println("请输入要复制到的文件路径");String destFileRoot = in.next();File destFile = new File(destFileRoot);//检查输入是否合法if (destFile.isDirectory()) {System.out.println("输入的不是普通文件(路径)");return;}//进行复制copyFile(srcFile, destFile);System.out.println("复制完毕");}//复制文件public static void copyFile(File srcFile, File destFile) throws IOException {try (InputStream inputStream = new FileInputStream(srcFile)) {try (OutputStream outputStream = new FileOutputStream(destFile)) {byte[] bytes = new byte[1024];while (true) {int n = inputStream.read(bytes);if (n == -1) break;outputStream.write(bytes, 0, n);//起点和要复制的长度}outputStream.flush();}}}
}

程序运行结果如下:
在这里插入图片描述

3.3 程序3:

  • 程序要求:扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)。

步骤如下:

  1. 提示读入数据
  2. 检查输入是否合法
  3. 查找指定文件

源码如下:


//案例三
//目的:扫描指定⽬录,并找到内容中包含指定字符的所有普通⽂件(不包含⽬录)
import java.util.*;
import java.io.*;
public class demo2 {public static void main(String[] args) {Scanner in = new Scanner(System.in);//提示读入数据System.out.println("请输入要搜索的目录:");String dirRoot = in.next();File dirFile = new File(dirRoot);if(!dirFile.isDirectory()){System.out.println("目录输入错误");return;}System.out.println("请输入要查找的指定内容");String serWord = in.next();//查找指定文件searchFile(dirFile,serWord);}private static void searchFile(File dirFile,String serWord) {File[] files = dirFile.listFiles();//这里不用判断为空for(int i = 0;i < files.length;i++){if(files[i].isFile()){//查找指定内容searchWord(files[i],serWord);}else if(files[i].isDirectory()){//递归进行查找searchFile(files[i],serWord);}}}private static void searchWord(File file, String serWord) {//查找文件内容StringBuilder sd = new StringBuilder();try(InputStream inputStream = new FileInputStream(file)){//读取数据while(true){int n = inputStream.read();if(n == -1) break;sd.append((char)n);}if(sd.indexOf(serWord) >= 0){//找到了System.out.println("文件路径为:" + file.getAbsolutePath());}} catch (IOException e) {throw new RuntimeException(e);}}
}

程序运行结果如下:
在这里插入图片描述

结语:
其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

在这里插入图片描述

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

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

相关文章

学习测试8-数据库mysql操作

下载配置mysql 网络博客 使用 在Linux里 1 service mysql start 启动服务 2 在Navicatt 中连接Linux服务器 3 第一步 将所有文件上传到/opt目录下 第二步 chmod 777 deploy-mysql.sh 第三步 ./deploy-mysql.sh4 service mysql status 查看状态是否安装成功 5 重启mys…

Typescript 模块小知识-global scope

问题表现 在编写ts代码的时候遇到一个问题, 表现为, 如果在某个ts工程中, 如果多个文件里面没有任何导出export或者是export default, 那么这些文件如果有const或者是let定义相同的声明都会报错如下 无法重新声明块范围变量 a/a.ts 和 index.ts 和 index2.ts 都没有进行expor…

基于Redisson 实现 Redis 分布式锁

代码示例&#xff1a; GetMapping("/testJmeter")public void testJmeter() {synchronized (this){int stock Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))if (stock > 0) {int realStock stock - 1;stringRedisTemplate.opsFo…

盘点:流媒体视频流协议与EasyCVR视频监控技术的深度融合

随着科技的不断进步&#xff0c;视频监控技术已经渗透到了社会的各个角落&#xff0c;从公共安全到企业运营&#xff0c;再到家庭安全&#xff0c;其应用范围日益广泛。而视频流格式作为视频监控技术的核心要素之一&#xff0c;其选择和应用对于确保视频传输的流畅性、清晰度和…

Python爬虫教程第3篇-解决使用reqeusts遇到的ProxyError异常

起因 问题出现在windows电脑上&#xff0c;我用mac执行程序的时候并不会报错&#xff0c;但是如果在windows上的时候&#xff0c;大部分windows电脑会报错&#xff0c;而有些版本低的windows电脑又不会报错。 异常栈信息 HTTPSConnectionPool, Cannot connect to proxy, no …

基于SpringBoot的校园疫情防控系统

你好&#xff0c;我是专注于计算机科学与技术的研究者。如果你对我的工作感兴趣或有任何问题&#xff0c;欢迎随时联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架&#xff0c;B/S架构 工具&#xff1a;Eclipse&#xff0c;Mav…

2024中国互联网大会 | 中国电信携手产业链伙伴推动低空经济行业创新与发展

低空经济是指以民用有人驾驶和无人驾驶航空器在低空空域内的各类飞行活动为牵引&#xff0c;辐射带动相关领域融合发展的综合经济形态。2024年全国两会&#xff0c;“低空经济”首次写入国务院政府工作报告。 赛迪顾问在《中国低空经济发展研究报告&#xff08;2024&#xff09…

python作业二

# 二进制转化为十进制 num input("num:")def binaryToDecimal(binaryString):he 0length len(binaryString)for i in range(length):he int(binaryString[i]) * 2 ** (length - i - 1)return heprint(binaryToDecimal(num))代码运行如下&#xff1a; import math…

特种设备管理为什么这么难?为何它是安全生产的重中之重?

随着工业化进程的加速和科技水平的不断提升&#xff0c;特种设备作为工业生产、公共服务和基础设施建设中的关键要素&#xff0c;其应用范围日益广泛&#xff0c;从大型压力容器、锅炉、电梯、叉车到压力管道、客运索道等&#xff0c;无一不渗透于我们日常生活的方方面面。然而…

【qt】QTcpSocket相关的信号

QTcpSocket可以在这里找到相关的信号 进行信号槽的关联 connect():这个信号在connectToHost()被调用并且连接已经成功建立之后发出 disconnected():该信号在套接字断开连接时发出 stateChanged(QAbstractSocket::SocketState socketState):每当QAbstractSocket的状态发生变化…

【若依前后端分离】通过输入用户编号自动带出部门名称(部门树)

一、部门树 使用 <treeselect v-model"form.deptId" :options"deptOptions" :show-count"true" placeholder"请选择归属部门"/> <el-col :span"12"><el-form-item label"归属部门" prop"dept…

音视频开发—使用FFmpeg从纯H264码流中提取图片 C语言实现

文章目录 1.H264码流文件解码流程关键流程详细解码流程详细步骤解析 2.JPEG编码流程详细编码流程详细步骤解析 3.完整示例代码4.效果展示 从纯H.264码流中提取图片的过程包括解码和JPEG编码两个主要步骤&#xff0c;以下是详细阐述 1.H264码流文件解码流程 关键流程 查找编解…

敏捷开发笔记(第10章节)--Liskov原则(LSP)

目录 1&#xff1a;PDF上传链接 10.1 Liskov替换原则&#xff08;LSP&#xff09; 10.2 一个违反LSP的简单例子 10.6 启发式规则和习惯用法 10.7 结论 1&#xff1a;PDF上传链接 【免费】敏捷软件开发(原则模式与实践)资源-CSDN文库 OCP背后的主要机制是抽象(abstraction…

group 与查询字段

需求 每周周一&#xff0c;统计菜单在过去一周&#xff0c;点击次数&#xff0c;和点击人数&#xff08;同一个人访问多次按一次计算&#xff09; 表及数据 日志表 CREATE TABLE t_data_log ( id varchar(50) NOT NULL COMMENT 主键id, operation_object varchar(500) DE…

【D3.js in Action 3 精译】1.3 D3 视角下的数据可视化最佳实践(下)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 ✔️ 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形1.2.3 Canvas 与 WebGL1.2.4 CSS1.2.5 JavaScript1.2.6 Node 与 JavaScript 框架1.2.7 Observable 记事…

我的世界1.21多种服务端开服教程,原版/Forge/Fabric/Paper/Mohist...,Minecraft开服教程

Minecraft&#xff08;MC&#xff09;1.21版多种服务端开服教程&#xff0c;我的世界1.21服务器搭建教程&#xff0c;MC原版/Forge/Fabric/Paper/Mohist服务端搭建教程&#xff0c;我的世界MOD/插件服开服教程。 本教程使用 Linux系统MCSManager 面板来搭建Minecraft服务器。 …

人工智能行业应用-垃圾识别一

垃圾识别应用主要体现在AI图像垃圾识别技术上&#xff0c;这是一种基于人工智能和计算机视觉技术的图像处理技术&#xff0c;广泛应用于各个领域以提高垃圾处理的效率和准确性。 1、垃圾识别效果图 2 垃圾识别任务分析 综合利用Python语言、Qt开发模块&#xff0c;OpenCV开发模…

数据结构(Java):单链表面试OJ题

1、题一&#xff1a;获取链表倒数第k个节点 . - 力扣&#xff08;LeetCode&#xff09; 1.1 思路解析 此题我们使用双指针法求解。 首先&#xff0c;我们要知道&#xff0c;倒数的第k个节点&#xff0c;距离倒数第一个节点还需要移动k-1次。 1.那么我们可以定义出两个指针&a…

SQL去重的四种方法

去重是指&#xff1a;查询的时候, 不显示重复&#xff0c;并不是删除表中的重复项 数据表&#xff1a; 方法1&#xff1a;distinct去重 作用&#xff1a;只能一列去重&#xff0c;当distinct后跟大于1个参数时&#xff0c;他们之间的关系是&&(逻辑与)关系&#xff0c;…

00 Debian字符界面如何支持中文

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian字符界面如何支持中文 《傅老师Debian知识库系列之00》——原创 前言 傅老师Debian知识库特点&#xff1a; 1、拆解Debian实用技能&#xff1b; 2、…