前言
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
文件IO就是通过代码对文件进行读写操作,这部分内容主要是概念上的东西比较多,理解起来比较简单,目录1和2的内容,铁汁们大概看下即可,重点是第3部分的内容,要掌握字节流和字符流对文件的读写操作
🎆个人主页:洋不写bug的博客
🎆所属专栏:JavaEE学习
🎆铁汁们对于JavaEE的各种常用核心语法,都可以在上面的JavaEE专栏学习,专栏正在持续更新中🏀,有问题可以写在评论区或者私信我哦~
1,基础知识
文件在计算机中是保存在硬盘上的,操作系统把硬盘操作进行了抽象封装,编程的时候,不会直接操作硬盘,而是通过“文件”的概念,进行间接的操作
在计算机中,是通过文件资源管理器来管理文件的,就类似于用树形结构来进行管理
如下图,就拿博主电脑来举例,在此电脑下有三个盘,每个盘里面有很多目录,每个目录(文件夹)又有目录和文件

可以通过路径来描述文件的位置,例如E:\jdk\lib\security,文件路径的每一级目录,都用 \ (反斜杠)隔开

这种以盘符(例如C、D、E盘)开头的,就是绝对路径;计算机中的每个文件的绝对路径是唯一的
其他的操作系统,例如Linux,MacOS,Android,都是采用 / (正斜杠)
Windows虽然默认是\ (反斜杠),但是也是可以识别正斜杠的
除了绝对路径,还有另一种表示方法,就是相对路径,相对路径通常是以. 或者…开头的,相对路径需要有一个参考系,也就是以哪个目录来进行相对的
例如这里,如果以E:\jdk作为基准路径的话,还是表示前面的security目录的话,相对路径就是.\lib\security,基准路径跟相对路径拼接在一起,就是绝对路径🐵

使用相对路径也可以表示基准路径的上一层,security目录是在lib目录中,那如果以security为基准路径,如何表示jdk目录中的release文件
可以通过…\表示目录或文件的上一层,这里的表示就是…\release

文件的种类,可以分为两个大类:
- 文本文件,存的都是字符串,必须依赖编码(ASCII,UTF8,GBK),如果是ASCII编码的话,那文件的所有内容全都严格按照 ASCII 编码规则转成字节存储(ASCII表如下图)
- 二进制文件,非文字类型的(例如视频,图片),直接存原始数据的字节,不涉及字符编码转译,例如视频,是通过音视频压缩算法标准 来翻译 01 字节,变成画面和声音
分辨一个文件是文本文件,还是二进制文件,最直接的方式就是用记事本打开试一下,如果是正常的文字,就是文本文件,如果是乱码,就是二进制文件
用记事本打开一个.java文件,会发现是正常的,这.java文件就是文本文件


用记事本打开一个图片,会发现里面就都是乱码,这是因为图片字节存的是像素、颜色、格式信息;但记事本按 UTF-8/GBK 字符编码

2,文件系统操作
文件系统操作包括创建文件,删除文件,移动文件,获取文件属性等
对文件进行操作,首先要创建File对象来表示文件的位置,创建时后面传入文件的相对路径或者绝对路径,代码如下:
File file = new File("D://test.txt");
注:在Java中使用反斜杠会被编译为转义字符,例如 \n就会标识为换行,因此,在传入文件的地址时,要不使用正斜杠,要不就使用两个反斜杠 //来表示
用相对路径来表示文件位置时,首先要知道当前的基准路径,使用IDEA的铁汁可以打开IDEA的控制台(左下角),按一下enter键,这里出来的路径就是当前的基准路径,这里博主的基准路径是E盘下的JavaEE目录

可以用文件对象调用下图中的方法,得到文件的各个路径

用绝对路径来创建个file,再用file调用这些方法看一下,拿到的父目录的路径也是绝对路径,拿到的文件路径、绝对路径、修饰过的绝对路径都是一样的
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\36689\\Desktop\\word文档\\前端\\1.html");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}

用相对路径来创建个file,拿到的父目录的路径和文件路径也变成了相对路径的形式,getAbsolutePath()非常有意思,就是用基准路径和相对路径拼接了一下,连中间的点都拼接上了
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File(".\\src\\main\\java");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}

下图中的方法是判断文件是否存在、是不是某种类型、以及创建,删除文件

刚刚博主在D盘下创建了一个test.txt文件,可以调用这些方法试一下:
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("D://test.txt");
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
}

铁汁们这里可以再创建一个目录来试下这三个方法,博主就不写了
文件的创建和删除,可以在JavaEE文件夹中创建一个文本文件,如下所示:
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File(".//test.txt");
file.createNewFile();
}
}

再写个delete,运行下,就会发现文件被删除了
file.delete();
deleteOnExit就是在JVM结束的时候才删除
写一段代码,用Scanner阻塞一下,也就是只有用户输入,程序才会结束,运行后打开JavaEE目录,会发现test.txt并没有被删除,只有在我们控制台输入,使程序结束,文件才会被删除·
package io;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File(".//test.txt");
file.createNewFile();
file.deleteOnExit();
Scanner scanner = new Scanner(System.in);
scanner.next();
}
}
deleteOnExit这种设计在什么时候会用到呢?
我们日常使用的office就用到了这个设计,这里博主桌面有个Java复习的文档,打开这个文档后,会发现同级目录下生成了一个文件名相同的文件(如下图),当我们正常关闭文件时,这个生成的隐藏文件就会被删除

这是因为我们对于文件的修改,如果没有保存的话,这些修改都是保存在内存上的,那如果电脑出问题蓝屏,或者断电,那文件没保存的内容就会丢失
为了避免这种情况数据的丢失,office就会在我们打开文件编辑时,把我们的修改保存下来,保存到隐藏文件中,隐藏文件是储存在硬盘中的,只有正常的退出隐藏文件才会被删除,如果是断电或者蓝屏,那隐藏文件就不会删除,再启动的时候,office就会通过隐藏文件的内容把之前改动的,未保存的内容还原回来
当然,硬盘可能也会出故障,因此,在实际工作中,最好还是把代码这些东西每天都上传到云端(Gitee/GitHub)
返回File对象下的所有文件名

如果File对象不是目录或者目录下没有文件,就会返回null,如下所示:
package io;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
System.out.println(Arrays.toString(file.list()));
System.out.println(Arrays.toString(file.listFiles()));
}
}

看下基准路径下(博主的基准路径是 E:\JavaEE)的文件名,list()方法返回的是文件名,listFiles()方法是以File对象形式返回的文件名,如下所示:
package io;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./");
System.out.println(Arrays.toString(file.list()));
System.out.println(Arrays.toString(file.listFiles()));
}
}


目录的创建

首先创建File对象,里面传入目录创建好后的位置,接着用mkdir进行创建即可,如下所示:
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./test");
file.mkdir();
}
}

如果是创建多级目录,也就是目录套目录,那用mkdir就不行了(运行完后什么都创建不出来),就要使用mkdirs,如下所示
package io;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./test/aaa/bbb/ccc");
file.mkdirs();
}
}

文件重命名

就是创建两个对象,一个表示原来文件的位置,一个表示希望更改后这个文件的位置(包括名字),接着用renameTo进行更改
可以对一个文件进行重命名,如下所示(代码中加个Scanner,在用户输入后才会往下继续执行,方便观察效果)
package io;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
if(!file.exists()){
file.createNewFile();
}
Scanner scanner = new Scanner(System.in);
scanner.next();
File file2 = new File("./test2.txt");
file.renameTo(file2);
}
}
用这个方法不仅可以实现文件的重命名,file2的路径也可以写成是其他地方的,这样就完成了文件剪切,在test目录中创建一个文本文件test2.txt,用renameTo实现一下文件的剪切,把这个文件再剪切进入JavaEE目录中,如下所示
package io;
import java.io.File;
import java.io.IOException;
public class Demo2 {
public static void main(String[] args) throws IOException {
File file = new File("./test/test2.txt");
File file2 = new File("./test2.txt");
file.renameTo(file2);
}
}


3,文件内容操作
文件内容操作包括读文件(硬盘的数据读到内存中)和写文件(把内存的数据写到硬盘)
要使用流对象来进行文件内容操作(不是一次性把所有数据全加载 / 写出,而是像水流一样,一点点、连续地传输数据,所以叫流对象),各种编程语言,围绕文件的操作,都称为“流”
比如需要读取100个字节:
- 一次读取100个字节,直接读完
- 一次读取50个字节,分两次读完
- 一次读取25个字节,分四次读完
…
Java中通过一系列的类,来表示流对象,主要分成了两个大类
- 字节流
InputStream(读) OutputStream(写)
读写数据的时候,以字节为基本单位,一次最少读取一个字节
在读取二进制文件的时候,通常使用字节流
- 字符流
Reader(读) Writer(写)
读写数据时,以字符为基本单位,一次最少读取一个字符(一个字符可能对应多个字节,具体看编码方式)
读写文本文件时,通常采用字符流
在IO过程中,输入和输出,是以CPU为基准,来进行考量的,如果方向是靠近CPU,就是输入(Input),如果方向是远离CPU,就是输出(Output)

1,字节流
创建文件输入流对象代码如下,传入要读取文件的位置(因为可能读取不到文件,所以要抛出个异常FileNotFoundException,它是IOException的子类)
InputStream是一个抽象类,因此不能直接用InputStream来创建实例,要用它的子类FileInputStream
创建文件流对象,这个过程,对应到操作系统中,就称为“打开文件”
public static void main(String[] args) throws FileNotFoundException {
InputStream inputStream = new FileInputStream("./test.txt");
}


读取调用inputStream的read方法,read方法里面如果不传入参数的话,就是一次读取一个字节
read方法读取字节,读取完成后以10进制的形式来返回,返回的也就是int类型,因此返回的最大值就是255(一个字节有8个比特位)
read()方法会从前往后读,当发现读取完了,读取不到了,就会返回-1
代码如下:
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo3 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./test2.txt");
while (true){
int c = inputStream.read();
if(c == -1){
//读取完毕了
break;
}
//转为16进制来看下
System.out.printf("0x%x\n",c);
}
}
}
在test2.txt文本文档里写上一个hello,再运行程序,如下所示,铁汁们可以用在线utf8编码器看一下,看hello编码后的十六进制是不是这样的


read方法的无参版本就是一次读取一个字节,有参版本就是里面传入一个字节数组,字节数组的大小自己定义
读取时要用while循环,会尝试把这个字节数组给填满
传入字节数组后,read方法返回的int值就不是字节转化为十进制了,而是读取到的有效字节数,如果读取完毕,就会返回-1
字节数组初始创建时,每个元素都是0,如果文件比较大的话,会分多次读取,前几次读取都会把字节数组给塞满
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo3 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./test2.txt");
while (true){
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
if(n == -1){
//读取完毕了
break;
}
System.out.println("n = " + n);
for(int i = 0;i < n;i++){
System.out.printf("0x%x\n",bytes[i]);
}
}
}
}

read方法还有一个三参数版本(如下图),那就是再传入一个off和len,尝试把参数中的字节数组填满,从off下标开始,最多填充len个(也就是一次只用数组的一部分)

使用IO流之后,要记得进行close(关闭资源),原因如下:
进程PCB中有一个属性,文件描述符表(顺序表),每次打开一个文件,都会在这个表里插入一个元素
文件描述符的长度是定长的,不能扩容,因此,光打开文件,不关闭文件,此时就会把表占满,后续再尝试打开文件,就会打开失败
写个readFile方法,里面包括文件的关闭,代码如下:
close方法要写在finally中,保证就算前面报错也能调用到
另外,如果没有读取到文件,inputStream为null,这时候就不需要关闭文件资源了(尝试用null调用close方法,就会报错),因此在finally中还要写if再去判断一下,代码如下所示:
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo5 {
private static void readFile(){
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./test.txt");
while (true){
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
if(n == -1){
break;
}
for(int i = 0;i < n;i++){
System.out.printf("0x%x\n",bytes[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(inputStream != null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
readFile();
}
}
但是上面的写法铁汁们可能也感觉到了,有一种代码很繁杂的感觉,不美观
其实有一种更短,更美观的写法:
使用 try-with-resources,这样能保证在try块结束后,自动调用每个资源的close方法,如果资源变量为null,那就会自动忽略,代码如下所示:
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo5 {
private static void readFile(){
try (InputStream inputStream = new FileInputStream("./test2.txt")){
while (true){
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
if(n == -1){
break;
}
for(int i = 0;i < n;i++){
System.out.printf("0x%x\n",bytes[i]);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
readFile();
}
}
字节输出流,就是往文件里面写内容,write方法有三个参数(如下图),一个是以十进制的形式来写单个字节,一个是以数组的形式,最后一个是只用数组的一部分,跟inputStream的类似

可以一个一个字节来写,往文件中写一个abcd,这里可以在write方法中传入十进制,十六进制,八进制,二进制,都会当作int类型来处理,在write方法中传入hello每个字符的十六进制,就能将hello写到.txt文件中,如下所示:

package io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo6 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./test.txt")) {
outputStream.write(0x61);
outputStream.write(0x62);
outputStream.write(0x63);
outputStream.write(0x64);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

接下来试下给write方法传入数组,在文件中写入一个“你好”

需要注意:byte数组中每个元素是一个字节,但是JVM处理十六进制是当成int类型来处理的,int类型是4个字节,大转小,就需要进行一下强制类型转换
package io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo6 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./test.txt")) {
byte[] bytes = {(byte) 0xe4,(byte)0xbd,(byte)0xa0,(byte)0xe5,(byte)0xa5,(byte)0xbd};
outputStream.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

这里我们会发现一个问题,那就是这次新写进去的数据,把原来的aaaa数据给覆盖掉了
其实严格来说不是新的数据把老数据给覆盖掉了,创建新的输出流对象,就会清空文件内容(铁汁们可以试下在main方法中单独运行这段代码,文件内容就会被清空)
OutputStream outputStream = new FileOutputStream("./test.txt")
要让新写入的数据写到原来的数据的后面,而不是覆盖掉呢,就需要创建输出流时,传入一个true参数,如下所示:
package io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo6 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./test.txt",true)) {
byte[] bytes = {(byte) 0xe4,(byte)0xbd,(byte)0xa0,(byte)0xe5,(byte)0xa5,(byte)0xbd};
outputStream.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

2,字符流
前面的字节流可以读取任意类型的文件,因为文件在硬盘上的存储本质上就是字节,不分文件类型,字符流则是一次读取一个字符,适用于文本文件
字节输入流的抽象类Reader,实现要用FileReader,使用跟字节流比较类似,read方法的无参版本一次读取一个字符,读取到的是字符的Unicode 值,读取完成就返回-1
这里test.txt文件中有个hello,用字符输入流读取,再以十六进制的形式来打印
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo01 {
public static void main(String[] args) {
try (Reader reader = new FileReader("D:\\Test\\test.txt")){
while(true){
int c = reader.read();
if(c == -1){
break;
}
System.out.printf("0x%x\n",c);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
字符流因为每次读取的是字符,所以如果read方法中传入数组的话,数组的类型是char[ ],read方法数组参数的返回值是读取到的字符个数
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo01 {
public static void main(String[] args) {
try (Reader reader = new FileReader("D:\\Test\\test.txt")){
while (true){
char[] chars = new char[1024];
int n = reader.read(chars);
if(n == -1){
break;
}
System.out.println("n = " + n);
for(int i = 0;i < n;i++){
System.out.println(chars[i]);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
字符输出流使用的是FileWriter,write中可以传入字符的Unicode码值,一次写入一个字符;也可以传入char数组,一次写入多个字符;还可以传入字符串,一次写入一个字符串(也可以传入三个参数,截取字符串或者字符数组的一部分写入)

之前的test.txt文件中写了个hello,这次就用传入字符数组往里面写入“字节跳动”,用传入字符串往里面写入"offer",创建输出流对象的时候传入个true,每次创建时不会清空文件,如下所示:
import java.io.*;
public class Demo01 {
public static void main(String[] args) {
try (Writer writer = new FileWriter("D:\\Test\\test.txt",true)){
char[] chars = {'字','节','跳','动'};
writer.write(chars);
writer.write("offer");
}catch (IOException e) {
e.printStackTrace();
}
}
}

3,缓冲流
?字节流和字符流会调用多次read和write方法,频繁的再磁盘(磁盘IO)读取或者写入数据,频繁触发磁盘IO,速度很慢
为了解决这个问题,Java中就加入缓冲流,降低了访问磁盘的次数,提升读写速度
- 读取文件数据时,缓冲流一次性从磁盘读取一大块数据,存入内存缓冲区,调用read读取时,直接读取内存缓冲区中的数据(速度非常快),缓冲区中的数据读取完了才访问磁盘,再次读取出一大块数据
- 往文件中写入数据时,先把数据写到内存缓冲区上,等到缓冲区满了或者数据传完了,关闭输出流资源了,再把缓冲区中的数据写入到磁盘中(也可以用flush方法,把当前缓冲区中的内容写到磁盘中)
缓冲流属于包装流,用缓冲流包含原始的流,字节流的缓冲流为BufferedInputStream和BufferedOutputStream,字符流的缓冲流为BufferedReader和BufferedWriter
缓冲流使用非常简单,创建缓冲流对象,包裹下原来的流对象即可(后续调用read或者write方法直接用缓冲流对象),就比如用字节流写入数组,代码如下:
import java.io.*;
public class Demo01 {
public static void main(String[] args) {
try (BufferedWriter bufferedWriter = new BufferedWriter( new FileWriter("D:\\Test\\test.txt",true))){
char[] chars = {'字','节','跳','动'};
bufferedWriter.write(chars);
bufferedWriter.write("offer");
}catch (IOException e) {
e.printStackTrace();
}
}
}
释放资源时,只需要关闭缓冲流资源,内部包装的流也会自动关闭,上面代码中,只需要bufferedWriter.close即可(这里我们用的时try - with - resource写法,能自动关闭资源,这点了解下即可)
用缓冲输出流(BufferedWriter/BufferedOutputStream)往文件中写入数据时,当内存缓冲区满了或者调用close方法关闭资源了,就会把内存缓冲区中的内容写到磁盘中,但是这样数据写入是具有延后性的
在实际开发的一些场景中不想延后时间太长,就可以过一段时间调用下flush方法,强制将内存缓冲区的内容写入到磁盘中
可以在代码中加个读取输入代码,如果加了flush方法,执行到读取输入时,"字节跳动"就已经写到文件中了,不加flush方法的话,执行到这里,"字节跳动"就没有写到文件中
import java.io.*;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
try (BufferedWriter bufferedWriter = new BufferedWriter( new FileWriter("D:\\Test\\test.txt",true))){
char[] chars = {'字','节','跳','动'};
bufferedWriter.write(chars);
bufferedWriter.flush();
Scanner scanner = new Scanner(System.in);
int input = scanner.nextInt();
bufferedWriter.write("offer");
}catch (IOException e) {
e.printStackTrace();
}
}
}
绝大多数读写文件场景下,都推荐使用缓冲流,缓冲流可以提高文件的读写效率🐵
4,文件搜索
代码要求:让用户输入文件名以及要搜索的目录,在目录中搜索该文件,如果查到就返回该文件的路径,并让用户选择是否要删除该文件(目录中是如果有多个名字相同的文件,将它们的路径一 一呈现给用户,让用户判断是否删除)
代码逻辑:
- 以字符串的形式读取用户输入的目录dir,以及要查询的文件名fileName,创建目录的File对象rootFile,判断rootFile是否为null,如果为null,说明该目录不存在,就退出查询
- 如果目录存在,就调用scanDir方法,传入rootFile和fileName,开始搜索目录,首先调用File的ListFiles方法,用File数组来接收返回值
- 接着遍历File数组,数组中的元素可能是文件,也可能是子目录,就递归调用scanDir方法搜索子目录中有没有符合要求的文件;如果数组中的元素是文件,就用contains方法比较文件名中是否包含fileName(如果选用equals方法,那用户就必须输入文件后缀名才能匹配上,就不太方便)
- 如果contains方法匹配上了,就调用tryDelete方法,传入要删除文件的File对象,打印出该文件的绝对路径,让用户判断是否要删除
import java.io.File;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要搜索的目录");
String dir = scanner.next();
System.out.println("请输入要查询的文件名");
String fileName = scanner.next();
File rootFile = new File(dir);
if(!rootFile.isDirectory()){
System.out.println("该目录不存在");
return;
}
scanDir(rootFile,fileName);
}
public static void scanDir(File rootFile,String fileName){
File[] files = rootFile.listFiles();
if(files == null){
return;
}
for(File file:files){
if(file.isDirectory()){
scanDir(file,fileName);
}else if(file.isFile()){
if(file.getName().contains(fileName)){
tryDelete(file);
}
}
}
}
public static void tryDelete(File file){
System.out.println("准备删除文件:" + file.getAbsolutePath());
Scanner scanner = new Scanner(System.in);
System.out.println("是否确认删除?(Y/N)");
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")){
file.delete();
System.out.println("删除成功!");
}
}
}
接下来测试下,博主在D盘中创建了一个Test目录,Test目录中又放了个Test1目录和Test2目录,Test1目录和Test2目录中都创建了个hello.txt文件,就来测试下能否查找出并删除,如下所示:

5,普通文件的复制
要求:让用户输入要复制文件(源文件)的路径和复制文件(目标文件)的路径(复制后复制文件叫什么名字,放在哪里)
代码逻辑:
- 以字符串的形式读取用户输入的源文件路径和目标文件的路径,创建源文件的File对象,判断下该对象是不是文件类型,如果不是的话,就提醒用户源文件不存在,并return
- 如果源文件对象是文件类型的话,继续判断目标文件路径是否合法,通过getParentFile拿到源文件的父目录对象,接着判断父目录对象是不是目录类型,如果不是,就提示目标文件的父目录不存在,并且return(从Java体系中来看,所有的文件都是储存在目录中的,哪怕文件直接储存在D盘下,调用getParentFile拿到的也是D盘的根目录D:\)
- 前两步校验都通过后,就调用copy方法,传入源文件对象和目标文件对象两个参数,在copy方法中用try-with-resources的方式来写代码,try的()中是可以输入流和输出流对象都创建的,后面销毁也是两个都销毁
- write方法中传入三个参数版本的,如果写一个参数版本的,直接传入数组,可能最后一次只读取到了200个字节,但是却写入了1024个字节的数据,就会有很多多余的字节
import java.io.*;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件的路径:");
String srcPath = scanner.next();
System.out.println("请输入目标文件的路径:");
String destPath = scanner.next();
File srcFile = new File(srcPath);
if(!srcFile.isFile()){
System.out.println("源文件不存在");
return;
}
File destFile = new File(destPath);
File parentFile = destFile.getParentFile();
if(!parentFile.isDirectory()){
System.out.println("目标文件的父目录不存在");
return;
}
copy(srcFile,destFile);
}
public static void copy(File srcFile,File destFile){
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while (true){
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if(len == -1){
break;
}
outputStream.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
接下来测试下代码,在D盘中搞个Test目录,目录里放一张老黄的照片,目标文件的路径还是Test目录,只是文件名改一下,如下所示:


结语💕💕
- 文件 IO 在编程里很常用,例如日志、配置、静态文件都是通过文件IO来读取的,如果数据不需要复杂查询、多条件筛选、事务这些功能,就可以不存到数据库中,通过文件IO存到文件中,通常开销会更低
- 大部分单机游戏的存档,都是通过文件IO存储在文件中的,例如黑猴,boss打过后想要重新打比较麻烦,有些大佬就找到存档文件的位置,修改下,就可以重新打了🐵
以上就是今天的所有内容啦~完结撒花~🥳🎉🎉

🎆个人主页:
8358



被折叠的 条评论
为什么被折叠?



