详细讲解 —— 文件操作(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());
    }
}

在这里插入图片描述

示例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());
    }
}

在这里插入图片描述

示例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());
    }
}

在这里插入图片描述

示例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());
    }
}

在这里插入图片描述

示例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());
    }
}

在这里插入图片描述

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();
            }
        }
    }
}

如果按照上面这样写就写的很繁琐,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();
        }
    }
}

虽然上面的代码中我们没有手动关闭文件的操作,但是 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();
        }
    }
}

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();
        }
    }
}

使用 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();
        }
    }
}

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();
        }
    }
}

使用 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();
        }
    }
}

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();
        }
    }
}

使用 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();
        }
    }
}

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();
        }
    }
}

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();
        }
    }
}

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;
        }
    }
}
  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT技术博主-方兴未艾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值