• 详细讲解 —— 文件操作(Java EE初阶)(万字长文)


    1 认识文件

    我们在平时说的文件一般都是指存储在硬盘上的普通文件,例如 txt,jpg,mp4,rar 等这些文件都可以认为是普通文件。

    在计算机中,文件是一个广义的概念,不只是普通文件,还可以包括目录文件(就是我们所说的文件夹)。

    1.1 普通文件

    普通文件是保存在硬盘上的,硬盘有机械硬盘和固态硬盘,现在主要用的还是机械硬盘,虽然固态硬盘的传输速度快,但是固态硬盘比机械硬盘要贵的多。

    机械硬盘

    机械硬盘的基本构造是,盘片(存储数据的介质)和磁头。

    机械硬盘一旦上电,里面的盘片就会高速运转,然后磁头再找到相应的数据。
    由于机械硬盘的硬件结构,盘片的转速越高,读写速度越快,因为物理工艺的限制,转速不可能无限的高,目前机械硬盘的读写速度已经停滞了,现在主要是往大容量的方向发展。

    固态硬盘

    固态硬盘的结构和机械硬盘的结构完全不同,固态硬盘就像是一个大号的U盘,固态硬盘的读写速度要比机械硬盘的读写速度要高很多。但是固态硬盘的价格更贵。

    目前的最好的固态硬盘的读写速度,可以和几十年前的CPU读写速度相比较了。

    文件分类

    站在程序员的角度可以把文件分成两类,一类是文本文件,一类是二进制文件。

    文本文件:里面存储的是字符。
    二进制文件:里面存储的是字节。

    针对这两种文件在编程的时候会存在差异。

    如果判断是哪一种文件,用记事本打开,如果打开之后是乱码,就是二进制文件,如果打开之后是文本就是文本文件。

    1.2 目录结构

    计算机里面保存和管理文件是通过 操作系统 中的 “文件系统” 模块来负责的。在文件系统中,一般是通过树形结构来组织磁盘上的文件和目录。
    其中的树形结构,就和二叉树很像,不过这里的 树形 结构是 N 叉的。

    在整个文件系统中,就是这种树形结构,普通的文件就是树的叶子结点,目录中可以包含普通文件,这个目录文件就相当于非叶子结点,有自己的子树。

    路径

    在操作系统中,就是通过 “路径” 这样的概念,来描述一个具体的文件/目录位置。由于是树形结构,其中根节点到每个节点的路径是唯一的。

    路径有两种描述风格:

    1. 绝对路径:以盘符开头的
      像:D:\java学习\java-topic-training
    2. 相对路径:以 . 或者 … 开头的,其中 . 表示当前路径,而 … 表示当前路径的父目录(上级路径)。
      谈到相对路径,必须有一个基准目录,相对路径就是从基准目录出发的。

    2 Java 中操作文件

    Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不
    代表真实存在该文件

    File 类中的构造方法和方法

    构造方法

    名称说明
    File(File parent, Stringchild)根据父目录 + 孩子文件路径,创建一个新的 File 实例
    File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
    File(String parent, Stringchild)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示

    方法

    示例1

    观察 get 系列的特点和差异

    返回值类型方法签名说明
    StringgetParent()返回 File 对象的父目录文件路径
    StringgetName()返回 FIle 对象的纯文件名称
    StringgetPath()返回 File 对象的文件路径
    StringgetAbsolutePath()返回 File 对象的绝对路径
    StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
    import java.io.File;
    import java.io.IOException;
    
    public class TestDome {
        public static void main(String[] args) throws IOException {
            File file = new File("./text.txt");
            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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    示例2

    普通文件的创建、删除

    返回值类型方法签名说明
    booleanexists()判断 File 对象描述的文件是否真实存在
    booleanisDirectory()判断 File 对象代表的文件是否是一个目录
    booleanisFile()判断 File 对象代表的文件是否是一个普通文件
    import java.io.File;
    import java.io.IOException;
    
    public class TestDome2 {
        public static void main(String[] args) throws IOException {
            File file = new File("C:\\Users\\我很浮躁\\Desktop\\text.txt");
            System.out.println(file.exists());
            System.out.println(file.isDirectory());
            System.out.println(file.isFile());
            System.out.println("======================");
            System.out.println("创建文件" + file.createNewFile());
            System.out.println("======================");
            System.out.println(file.exists());
            System.out.println(file.isDirectory());
            System.out.println(file.isFile());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    示例3

    观察 createNewFile() 的现象

    返回值类型方法签名说明
    booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
    booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
    voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
    import java.io.File;
    import java.io.IOException;
    
    public class TestDome3 {
        public static void main(String[] args) throws IOException {
            File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
            System.out.println(file.exists());
            System.out.println(file.createNewFile());
            System.out.println(file.exists());
            System.out.println(file.delete());
            System.out.println(file.exists());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    示例4

    观察目录的创建

    返回值类型方法签名说明
    String[]list()返回 File 对象代表的目录下的所有文件名
    File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
    booleanmkdir()创建 File 对象代表的目录
    booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
    import java.io.File;
    
    public class TestDome4 {
        public static void main(String[] args) {
            //这个目录不存在
            File file = new File("C:\\Users\\我很浮躁\\Desktop\\text");
            System.out.println(file.isFile());
            System.out.println(file.isDirectory());
            System.out.println(file.mkdir());
            System.out.println(file.isFile());
            System.out.println(file.isDirectory());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    示例5

    观察文件重命名

    返回值类型方法签名说明
    booleanrenameTo(Filedest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
    booleancanRead()判断用户是否对文件有可读权限
    booleancanWrite()判断用户是否对文件有可写权限
    import java.io.File;
    
    public class TestDome5 {
        public static void main(String[] args) {
            File file = new File("C:\\Users\\我很浮躁\\Desktop\\text");
            File dest = new File("C:\\Users\\我很浮躁\\Desktop\\dest");
            System.out.println(file.exists());
            System.out.println(dest.exists());
            file.renameTo(dest);
            System.out.println(file.exists());
            System.out.println(dest.exists());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    3 文件内容的读写 —— 数据流

    文件内容:
    1)打开文件,2)读文件,3)写文件,4)关闭文件

    针对文件的读写,Java标准库中提供了一组类,按照文件的内容分成两个系列:

    1. 字节流对象,针对二进制文件,是以字节为单位进行读写的。有两个抽象类,读:InputStream,写:OutputStream,我们往往使用的他们的子类,FileInputStream 和 FileOutputStream。
    2. 字符流对象,针对文本文件,是以字符为单位进行读写的。有两个抽象类,读:Reader,写:Writer,我们要使用的子类为 FileReader 和 FileWriter。

    3.1 InputStream 概述

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

    使用 read() 来读

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class TestDome6 {
        public static void main(String[] args) {
            InputStream inputStream = null;
            try {
                //1.打开文件
                inputStream = new FileInputStream("E:\\test.txt");
                while(true){
                    //2.读文件,直到把文件读完,文件读完返回-1;
                    int buffer = inputStream.read();
                    if(buffer == -1){
                        break;
                    }
                    System.out.println(buffer);
                }
            } catch (IOException e) {
                //如果没有文件,和,读的时候异常就会报错。
                e.printStackTrace();
            }finally {
                //3. 关闭文件。
                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

    如果按照上面这样写就写的很繁琐,Java中提供了一个机制(逻辑),代码如下:

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class TestDome7 {
        public static void main(String[] args) {
            //1.打开文件
            try(InputStream inputStream = new FileInputStream("E:\\test.txt")) {
                //2.读文件
                while(true){
                    int buffer = inputStream.read();
                    if(buffer == -1){
                        break;
                    }
                    System.out.println(buffer);
                }
            } 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 / catch 走完就自动关闭文件。这样代码就简化了很多。

    使用 read(byte[] b) 来读

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class TestDome8 {
        public static void main(String[] args) {
            try(InputStream inputStream = new FileInputStream("E:\\test.txt")) {
                byte[] buffer = new byte[1024];
                while(true){
                    //这里面的 buffer 是输出型参数(把要带回来的值放到buffer数组中)
                    //其中的返回值为读取字符的长度。
                    int length = inputStream.read(buffer);
                    //当读取字符长度为 -1 时,文章内容读完了。
                    if(length == -1){
                        break;
                    }
                    for(int i=0; i<length; i++){
                        System.out.println(buffer[i]);
                    }
                }
            } 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

    3.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(刷新)操作,将数据刷到设备中。

    使用 write(int b) 写入数据

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class TestDome9 {
        public static void main(String[] args) {
            try(OutputStream outputStream = new FileOutputStream("E:\\test.txt")) {
                outputStream.write('a');
                outputStream.write('b');
                outputStream.write('c');
                outputStream.write('d');
                outputStream.write('e');
                outputStream.write('f');
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用 write(byte[]b) 写入数据

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class TestDome10 {
        public static void main(String[] args) {
            try(OutputStream outputStream = new FileOutputStream("E:\\test.txt")) {
                byte[] buffer = {'a', 'b', 'c', 'd'};
                outputStream.write(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.3 Reader 概述

    使用 read(int b) 来读

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    public class TestDome11 {
        public static void main(String[] args) {
            try(Reader reader = new FileReader("E:\\test.txt")) {
                while(true){
                    int buffer = reader.read();
                    if(buffer == -1){
                        break;
                    }
                    System.out.println(buffer);
                }
            } 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

    使用 read(char[] buffer) 来读

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    public class TestDome14 {
        public static void main(String[] args) {
            try(Reader reader = new FileReader("E:\\test.txt")) {
                char[] buffers = new char[1024];
                while(true){
                    int length = reader.read(buffers);
                    if(length == -1){
                        break;
                    }
                    String str = new String(buffers, 0, length);
                    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

    3.4 Writer 概述

    使用 write(char ch) 写入数据

    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class TestDome13 {
        public static void main(String[] args) {
            try(Writer writer = new FileWriter("E:\\test.txt")) {
                writer.write('a');
                writer.write('a');
                writer.write('a');
                writer.write('a');
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用 write(String string) 写入数据

    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class TestDome12 {
        public static void main(String[] args) {
            try(Writer writer = new FileWriter("E:\\test.txt")) {
                writer.write("hello world");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4 小程序练习

    4.1 示例1

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

    import java.io.File;
    import java.io.IOException;
    import java.util.Scanner;
    
    public class TestDome15 {
        public static void main(String[] args) {
            //输入参数
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输出要查找的文件名:");
            String rootDirPath = scanner.next();
            System.out.println("请输入要删除的文件");
            String delFile = scanner.next();
            //1.打开文件目录
            File rootDir = new File(rootDirPath);
            if(!rootDir.isDirectory()){
                System.out.println("输入的目录名非法");
            }
            //因为目录是树形结构,使用递归循环遍历每一个文件
            scanFile(rootDir, delFile);
        }
    
        private static void scanFile(File rootDir, String delFile) {
            //列出目录中的文件和目录
            File[] files = rootDir.listFiles();
            if(files == null){
                return;
            }
            for (File f: files) {
                //如果是文件就要判断是否和我们要找的文件名相同
                if(f.isFile()){
                    if(f.getName().contains(delFile)){
                        //如果相同就删除文件
                        deleteFile(f);
                    }
                }else if(f.isDirectory()){
                    //如果是目录就直接递归调用
                    scanFile(f, delFile);
                }
            }
        }
    
        private static void deleteFile(File f) {
            try {
                //判断文件是否要删除
                System.out.println(f.getCanonicalPath() + "确定要删除这个文件吗(Y/N )");
                Scanner scanner = new Scanner(System.in);
                String buffer = scanner.next();
                if(buffer.equals("y") || buffer.equals("Y")){
                    f.delete();
                    System.out.println("删除成功");
                }else{
                    System.out.println("删除失败");
                }
            } 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    4.2 示例2

    进行普通文件的复制

    import java.io.*;
    import java.util.Scanner;
    
    public class TestDome16 {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入文件所在的路径:");
            String source = scanner.next();
            System.out.println("请输入文件要复制到的路径");
            String dest = scanner.next();
            File sourceFile = new File(source);
            if(!sourceFile.isFile()){
                System.out.println("输入错误");
                return;
            }
            try(InputStream inputStream = new FileInputStream(source)) {
                try(OutputStream outputStream = new FileOutputStream(dest)){
                    byte[] buffers = new byte[1024];
                    while(true){
                        int length = inputStream.read(buffers);
                        if(length == -1){
                            break;
                        }
                        outputStream.write(buffers, 0, length);
                    }
                }
            } 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

    4.3 示例3

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

    import java.io.*;
    import java.util.Scanner;
    
    public class TestDome17 {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入要扫描的路径:");
            String source = sc.next();
            System.out.println("请输入要查找的文字:");
            String word = sc.next();
            File sourceFile = new File(source);
            if(!sourceFile.isDirectory()){
                System.out.println("输入非法");
                return;
            }
            //扫描目录中的文件,递归调用
            scanFile(sourceFile, word);
        }
    
        private static void scanFile(File sourceFile, String word) {
            File[] files = sourceFile.listFiles();
            if(files == null){
                return;
            }
            for(File f: files){
                //查看是的是文件,如果是文件就看看有没有我们指定的字符串
                if(f.isFile()){
                    if(containWord(f, word)){
                        try {
                            //如果包含就打印文件的路径
                            System.out.println(f.getCanonicalPath());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    //如果是文件就递归调用
                }else if(f.isDirectory()){
                    scanFile(f, word);
                }
            }
        }
    
        private static boolean containWord(File f, String word) {
            StringBuffer stringBuffer = new StringBuffer();
            try(Reader reader = new FileReader(f)) {
                char[] buffers = new char[1024];
                while(true){
                    int length = reader.read(buffers);
                    if(length == -1){
                        break;
                    }
                    stringBuffer.append(buffers, 0, length);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(stringBuffer.toString().contains(word)){
                return true;
            }else{
                return false;
            }
        }
    }
    
    • 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
  • 相关阅读:
    我的私人笔记(安装hbase)
    【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,不同信道估计方法以及不同调制方式对比。
    linu入门9—操作系统正则符号
    制作网线——双绞线
    UE5安装visual studio integration和Cesium失败问题
    Shell 文本三剑客 (grep、sed、awk)
    MySQL索引为什么选择B+树,而不是二叉树、红黑树、B树?
    158_模型_Power BI 使用 DAX + SVG 打通制作商业图表几乎所有可能
    搭建Umijs环境并创建一个项目 介绍基本操作
    Python爬虫实战:从API获取数据
  • 原文地址:https://blog.csdn.net/IT_Infector/article/details/125838441