• 文件操作--I/O


    文件IO流

    认识文件

    我们常说的I/O,指的就是,Input/Output。那么针对硬盘持久化存储的I/O设备,当我们进行数据保存的时候,往往保存的是独立成一个个单位进行保存,而不是保存一个整体。这种独立的单位就被抽象成文件的概念。

    从狭义来说,我们通常说的文件,指的是存储在硬盘上的“数据” 普通文件。

    从广义来说,文件概念会广泛,操作系统会把很多的 硬件设备/软件资源,也给抽象成文件,例如,读写 网卡,网络编程,就操作系统而言会把这些硬件设备/软件资源也给抽象成文件。

    抽象文件能够让系统更加的统一和管理。

    文件除了有数据内容外,还有一部份的信息,such as 文件名,日期,类型,文件大小等并不作为文件的数据而存在,我们把这部分的信息称之为文件的 元信息

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TG0ns44l-1656834666134)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220703152947359.png)

    通过这一个个的文件,我们可以将这些文件给组织起来,更方便用户使用它,逻辑上也更好的理解它。

    同时,随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,一种合乎自然
    的想法出现了,就是按照层级结构进行组织 —— 也就是我们数据结构中学习过的树形结构。这样,一
    种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录(directory)的
    概念。

    Windows的树形结构组织:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohdpyTlw-1656834666135)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220703153700751.png)]

    Linux上的树形结构组织:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZ490B5A-1656834666137)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220703153826720.png)]

    文件的路径

    文件路径分为两种:

    1、绝对路径(以 / 开头 或者 以盘符开头):从树根出发,依次记录下中间经历的路径。

    2、相对路径(以 . 或者 … 开头):从当前工作目录出发,中间经历的路径。

    / 基准所在的最顶级目录即根目录

    ./ 基准所在的当前目录

    …/ 基准所在的当前目录的上一级目录(当前目录的父级目录)

    文件的类型:

    文本文件,操作文件的时候,基本单位是 “字符”

    二进制文件,操作文件的时候,基本单位是“字节”

    如何在java中操作文件??

    1、在文件系统的层面上来操作文件

    ​ 创建文件、删除文件、创建目录、拷贝文件、重命名文件…

    2、操作文件的内容(操作文件里面保存的数据)

    ​ 读文件,写文件。

    在java标准库中,提供了 java.io.File 这样的类,来描述以及操作文件;(io----->input/output,输入输出)

    一、File

    ​ 通过一个File类实例,就可以描述一个具体的文件。

    • 构造方法:
    File(File parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例
    File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者 相对路径
    File(String parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用 路径表示
    • 方法:
    修饰符及返回 值类型方法签名说明
    StringgetParent()返回 File 对象的父目录文件路径
    StringgetName()返回 FIle 对象的纯文件名称
    StringgetPath()返回 File 对象的文件路径
    StringgetAbsolutePath()返回 File 对象的绝对路径
    StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径

    重点说一下 getAbsolutePath() 和 getCanonicalPath():

    1、getAbsolutePath() 表示的是绝对路径,eg:c:/././././text.txt

    2、getCanonicalPath() 表示修饰过的绝对路径,此时就会对绝对路径进行修改简介,eg:C:/text.txt

    看一下方法的实现:

    import java.io.File;
    import java.io.IOException;
    
    
    public class Demo01 {
        public static void main(String[] args) throws IOException {
            File file1 =new File("d:/text.txt"); // 绝对路径
            System.out.println(file1.getParent()); 
            System.out.println(file1.getName());
            System.out.println(file1.getPath());
            System.out.println(file1.getAbsolutePath());
            System.out.println(file1.getCanonicalPath());
    
            System.out.println("-------------------------------------");
            File file2 = new File("./text.txt");// 有File对象,但是并不代表真实存在该文件。
            System.out.println(file2.getParent());
            System.out.println(file2.getName());
            System.out.println(file2.getPath());
            System.out.println(file2.getAbsolutePath());
            System.out.println(file2.getCanonicalPath());
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Goxvu8dP-1656834666138)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220627195046702.png)]

    这是打印出来的结果,大家亦可以注意到,在IDEA里面我们用的是 “/” ,但是打印出来是 “\”

    大部分都是使用 ”/ “ 作为目录之间的分隔符;但是对于 Windows来说 “\” 作为分隔符,这要扯就要扯到Windows的历史了…(锅是产品经理的锅🕷🕷🕷🕷🕷🕷)

    所以记住结论:

    • Windows 是 “\” 作为分割符
    • Linux/Mac 大部分的操作系统都是 “/” 作为分隔符

    ❓❓❓❓ 为啥大部分的操作系统都是使用 “/” 分割符??

    很简单,在编程语言中 “\” 表示的是转义字符亚,转义字符都是有特殊含义的,可能在编程中你非要写 “\” 那么你就得加两个“\ \” 。

    booleanexists()判断 File 对象描述的文件是否真实存在
    booleanisDirectory()判断 File 对象代表的文件是否是一个目录
    booleanisFile()判断 File 对象代表的文件是否是一个普通文件
    booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返 回 true
    booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
    String[]list()返回 File 对象代表的目录下的所有文件名
    File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象 表示
    import java.io.File;
    import java.io.IOException;
    
    public class Demo02 {
        public static void main(String[] args) throws IOException {
            File file1 = new File("./text.txt");
            System.out.println(file1.exists()); //false
            System.out.println(file1.isDirectory()); // false
            System.out.println(file1.isFile()); // false
            System.out.println("=========================");
    
            System.out.println(file1.createNewFile()); // 创建了一个新文件   true
            System.out.println(file1.exists()); // true
            System.out.println(file1.isDirectory()); // false
            System.out.println(file1.isFile()); //true
            System.out.println("删除文件之前");
            System.out.println(file1.delete()); // true
            System.out.println("删除文件之后");
            System.out.println(file1.exists()); // false
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    注意:

    1、file.createNewFile() 创建文件的操作是不一定成功的,可能会导致没有权限,创建失败,这里就需要直接抛出IOException异常了。

    2、delete()也不一定轻易删除文件成功,一方面是权限,一方面是你的文件路径不对

    voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行

    这个方法可以说一下,这个方法操作不是立即删除,而是等程序退出的时候再删除,这个功能一般适用于临时文件。

    import java.io.File;
    import java.io.IOException;
    import java.util.Scanner;
    import java.util.concurrent.TimeUnit;
    
    public class Demo04 {
        public static void main(String[] args) throws IOException, InterruptedException {
            File file1 = new File("./111.txt");
            file1.createNewFile();
            file1.deleteOnExit();
    
            TimeUnit.MILLISECONDS.sleep(3000);//等待3000毫秒
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-88a9oo9W-1656834666139)(D:\常用文件夹\资料信息图片\Typora-image\Image\deleteOnExit.gif)]

    可以从动画中看出 deleteOnExit() 这个功能 会在程序结束的时候把临时文件删除

    booleanmkdir()创建 File 对象代表的目录
    booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目 录

    创建目录,mkdir()只能创建一级目录;

    mkdirs()创建多级目录 ,eg:./111/222/333/

    import java.io.File;
    
    public class Demo05 {
        public static void main(String[] args){
            File f1 = new File("./111/22/33/");
            f1.mkdirs();
            System.out.println(f1.isDirectory());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2FdLyBI4-1656834666141)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220628161243185.png)]

    booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操 作

    重命名文件。

    import java.io.File;
    
    public class Demo06 {
        public static void main(String[] args) {
            File f1 = new File("./text.txt");
            File f2 = new File("./text2.txt");
            f1.renameTo(f2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    把文件 f1 的名字重命名为 f2 的名字。

    二、字节流

    2.1 InputStream (输入流)

    字节流中 InputStream(负责读) / OutputStream(负责写) ,这个系列是针对二进制文件进行读写,基本单位都是字节。

    主要的功能实现:

    1、打开文件(构造方法)

    2、读文件

    3、写文件

    4、关闭文件

    方法:

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

    2.1.1 read() 读文件

    • 读文件
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class Demo07 {
        public static void main(String[] args) {
            /**
             * InputStream 输入流,是一个抽象类,要使用需要具体实现类。
             * FileInputStream 是一个典型的实现
              */
            InputStream inputStream = null;
            try {
                inputStream = new FileInputStream("./text.txt");
                // 需要读文件,就需要字节一个一个的读出来
                while (true){
                    // 为什么是 int接收 ,看一下 read()的注释
                    int b = inputStream.read();
                    if (b == -1){
                        break;
                    }
                    System.out.println(b);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    InputStream 抽象类起到总览全局的作用,要使用需要具体是实现类,FileInputStream就是只关心文件中的读取,所以FileInputStream就是一个典型的类实现。

    签名说明
    FileInputStream(File file)利用 File 构造文件输入流
    FileInputStream(String name)利用文件路径构造文件输入流

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-POPdrLmP-1656834666142)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220628170423811.png)]

    重点说一说 read()方法,该操作的核心方法提供了三个版本 read(byte[] b) 、read()、read(byte[] b ,int off , int len) ,现讲讲这段代码的版本。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2yfpHtx-1656834666142)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220628171358314.png)]

    ❔❔❔❔❔ 尽然是读字节 为啥不用 byte ,而用 int 尼???

    进入 read()源码,用的是 int 类型接收;int 的 Range (0~255),byte 的 Range(-128~127),此处不许要参与运算,因为读出来的只是一个单纯的 字节。通过翻译一颗得知读取到文件末尾没有字节就返回 -1 ,得到 -1 就表示这个文本文件读完了。

    在进行各种各样输入输出的操作中,都可能会产生异常,读文件的时候,可能读着读着就磁盘就坏了,这种时候就抛出IOException异常。

    运行结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmKgVv4K-1656834666143)(D:\常用文件夹\资料信息图片\Typora-image\Image\image-20220628172405013.png)]

    我们是以 字节流 来读,那么读出来的结果就是这些字符的 ASCII 码;如果存的是中文,读出来的中文就是每个字节的编码(GBK/UTF8),因为我的IDEA是UTF8所以读出来一个中文三个字节。

    第二种写法(简单不冗长):

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class Demo08 {
    
        public static void main(String[] args) {
            try (InputStream inputStream = new FileInputStream("./text.txt")){
                while(true){
                    int b = inputStream.read();
                    if (b == -1){
                        break;
                    }
                    System.out.println(b);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    把要释放的资源放到 try()里面,然后就会自动调用到关闭,无论是否异常,这种写法 JDK1.5就有了。

    前提是 try()里的对象能够实现 Closeable 接口,但是文件流对象,都是实现了 Closeable 接口的。

    2.1.2 read(byte[] b) 读文件

    这个方法一次读若干个字节,尽可能的填满这个字节数组,方法返回值是实际读到的字节数。

    1、此处的参数 b 是一个用来表示方法返回结果的参数,称之为“输出型参数”。

    2、如果文件中剩余的数据比较多,超过了参数数组的长度,就会直接返回数组长度(把数组填满)。

    3、如果文件中剩余的数据比较少,不超过参数数组的长度,此时就会直接返回实际的元素个数。

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class Demo09 {
        public static void main(String[] args){
            // 尝试一次性读取 1024 个字节
            try(InputStream inputStream = new FileInputStream("./text.txt")) {
                byte[] buffer = new byte[1024];
                while (true){
                    // 因为实际长度 < 1024 所以返回的是实际元素个数
                    int len = inputStream.read(buffer);
                    if (len == -1){
                        break;
                    }
    
                    // 这个操作是把 1024个数组全部遍历打印出来
    //                for (byte b: buffer){
    //                    System.out.println(b);
    //                }
    
                    // 如需只打印字符串,直接转换即可
                    String str = new String(buffer,0,len,"UTF-8");
                    System.out.println(str);
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.1.3 通过字符读

    InputStream 这个是按照字节来读取,我们也可以通过 Reader/ FileReader 按照字符来读。

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class Dome10 {
        public static void main(String[] args){
            try(FileReader fileReader = new FileReader("./text.txt")) {
                while(true){
                    // 这里也是通过 int 来接收 字符
                    int c = fileReader.read();
                    if (c == -1){
                        break;
                    }
                    // 需要转换成 char 类型才行,不然就是 ascii 码值。
                    System.out.println((char)c);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    总结下来:读字节,一次读的是一个 byte ,读字符,一次读的是一个 char。

    2.2 OutputStream(输出流)

    按照字节来写:OutputSteam / FileOutputStream。

    修饰 符及 返回 值类 型方法签名说明
    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 打开的文件就会默认清空之前这个文件。

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class Demo11 {
        public static void main(String[] args){
            try (OutputStream outputStream = new FileOutputStream("./text.txt")){
                outputStream.write(97);
                outputStream.write(98);
                outputStream.write(99);
                outputStream.flush();
            } catch (FileNotFoundException e) { // FileNotFoundException 是 IOException的子类是可以合并简化的。
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    按照字符来写:Writer / FileWriter

    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    public class Demo12 {
        public static void main(String[] args){
            try(Writer writer = new FileWriter("./text.txt")) {
                writer.write('x');
                writer.write('x');
                writer.write('x');
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 总结

    1、读文件,InputStream / FileInputStream / Reader / FileReader ------> read

    2、写文件,OutputStream / FileOutputStream / Writer / FileWriter ------> write

    三、案例实现

    3.1 案例一 (删除符合条件文件)

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

    步骤:
    1、让用户输入一个带扫描的路径;
    2、判断路径是否合法;
    3、输入要删除的文件;
    4、遍历当前目录,遍历所有文件名和带删除文件名匹配的文件;
    5、进行删除操作,把result里找到所有文件,都依次进行删除。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    public class DeleteFile {
        public static void main(String[] args) throws IOException {
            // 1、先让用户输入带扫描的目录路径
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入要扫描的目录路径");
            String rootDirPath = scan.next();
            // 2、判断文件的路径合不合法
            File rootDir = new File(rootDirPath);
            if (!rootDir.isDirectory()){
                System.out.println("你扫描的目录路径不合法,请重新输入目录路径");
                return;
            }
    
            // 3、输入要删除的文件
            System.out.println("请输入你要删除的文件名:");
            String deleteFileName = scan.next();
    
            // 4、遍历当前这个目录下的文件,找到所有文件名和待删除文件匹配的文件。
            List<File> result = new ArrayList<>();
            //    通过 scanDir 这个方法, 把所有和 deleteFileName 匹配的文件, 都给找出来, 放到 result 中.
            scanDir(rootDir, deleteFileName ,result);
    
            // 5、进行文件的删除操作,把result 里面找到的文件,依次删除.
            for (File f:result){
                System.out.println(f.getCanonicalPath()+"该文件是否删除?Y/N");
                String choice = scan.next();
                if (choice.equals("Y")){
                    f.delete();
                    System.out.println(f.getCanonicalPath()+"该文件删除成功!!");
                }
            }
        }
    
        public static void scanDir(File rootDir ,String deleteFileName , List<File> result) throws IOException {
            // 1、首先罗列出 rootDir 目录下的文件 ----》 需要用到方法 :listFiles()
            File[] files = rootDir.listFiles();
            // 遍历,扫描所有文件
            for (File f:files) {
                System.out.println("扫描了文件:"+f.getCanonicalPath());
                // 判断是否是普通文件
                if(f.isFile()){
                    // 判定文件名字
                    if (f.getName().equals(deleteFileName)){
                        result.add(f);
                    }
                    // 文件是目录就递归
                } else if(f.isDirectory()) {
                    scanDir(f,deleteFileName,result);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    3.2 案列二 (普通文件复制)

    步骤:
    1、输入需要复制的原文件以及目标文件;
    2、判定 原文件是否存在;
    3、进行复制操作,用到InputStream(负责读),OutputStream(负责写);
    
    • 1
    • 2
    • 3
    • 4
    import java.io.*;
    import java.util.Scanner;
    
    public class CopyFile {
        public static void main(String[] args){
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入原文件的路径");
            String src = scan.next();
            System.out.println("请输入目标文件的路径");
            String destination = scan.next();
    
    
            // 判断 src 是否存在
            File srcFile = new File(src);
            if (!srcFile.exists()) {
                System.out.println("当前路径文件不存在,请重新输入");
                return;
            }
    
            // 进行复制操作,关键用到 InputStream and OutputStream
            try(InputStream inputStream = new FileInputStream(src)) {
                try(OutputStream outputStream = new FileOutputStream(destination)) {
                    byte[] buffer = new byte[1024];
                    while(true){
                        int len = inputStream.read(buffer);
                        if (len == -1){
                            break;
                        }
                        // 读取数据
                        outputStream.write(buffer,0,len);
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    3.3 案例三 (找到文件中包含指定字符)

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

    步骤:
    1、输入目录和要查找的内容;
    2、进行递归遍历,找到所有符合要求的文件(这里面实际了两个方法);
    3、打印找到的结果;
    
    • 1
    • 2
    • 3
    • 4

    其实就是案例一 和 案例二 的结合。

    import java.io.*;
    import java.sql.Array;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    public class FindContentFile {
        public static void main(String[] args) throws IOException {
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入要遍历的目录");
            String rootDirPath = scan.next();
            System.out.println("请输入要指定查找的内容");
            String content = scan.next();
            // 判断一下 目录路径是否存在
            File rootDir = new File(rootDirPath);
            if (!rootDir.exists()){
                System.out.println("您输入的路径不存在!");
                return;
            }
            // 存在进行递归遍历
            List<File> results = new ArrayList<>();
            scanDirWithContent(rootDir,content,results);
    
            // 打印文件结果
            for (File f:results){
                System.out.println(f.getCanonicalPath());
            }
        }
    
        private static void scanDirWithContent(File rootDir,String content,List<File> results){
            // 列出 rootDir 都有哪些文件
            File[] files = rootDir.listFiles();
            if (rootDir == null){
                return;
            }
    
            // 2. 依次遍历每个文件, 进行判定. 如果是目录就进行递归.
            for (File f:files){
                if(f.isFile()){
                    // 是普通文件就进行判断 content 是否包含其中
                    if (isContentExist(f,content)){
                        results.add(f);
                    }
                } else if(f.isDirectory()){
                    scanDirWithContent(f,content,results);
                }
            }
        }
    
        private static boolean isContentExist(File f, String content) {
            StringBuilder sb = new StringBuilder();
            try(Reader reader = new FileReader(f)){
                while (true){
                    int c = reader.read();
                    if (c == -1){
                        break;
                    }
                    sb.append((char) c);
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // indexOf 如果找到了子串,就返回正常的下标,没找的就返回-1;
            return sb.indexOf(content) != -1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    注意:这段代码是低效代码,尽量不要在太复杂的目录下或者大文件下实验 。


    这篇帖子介绍了文件操作以及I/O流的基本用法和基础概念,都是必掌握。

    铁汁们,觉得笔者写的不错的可以点个赞哟❤🧡💛💚💙💜🤎🖤🤍💟,收藏关注呗,你们支持就是我写博客最大的动力!!!!

  • 相关阅读:
    markdown语法(更新中)
    数据库定时备份winserver2012篇
    将docker镜像打成tar包
    superset 难用到爆炸 metabase反而更加友好
    SQL Server 数据库之导入导出数据
    记一次阿里云日志导致的服务宕机
    Linux查看防火墙状态及开启关闭命令-转载记录
    GPT-3被超越?解读低能耗、高性能的GlaM模型
    CSC关于进一步优化国家公派出国留学服务管理工作的通知
    Spring MVC程序开发@RequestMapping 注解介绍
  • 原文地址:https://blog.csdn.net/qq_54219272/article/details/125585418