Java文件操作 —— IO

文件操作 — IO

  • 1 认识文件
    • 1.1 文件存储在硬盘
    • 1.2 文件的分类
    • 1.3 关于目录结构
    • 1.4 文件路径
  • 2 Java 中操作文件
    • 2.1 File 对象构造方法
    • 2.2 方法
      • 2.2.1 代码示例
        • get 系列方法
        • 普通文件的创建
        • 普通文件的删除
        • deleteOnExit
        • 创建文件夹(目录)
        • mkdirs()
        • 文件重命名
  • 3 文件内容的读写 —— 数据流
    • 3.1 InputStream 概述
      • 方法
      • 说明
    • 3.2 FileInputStream 概述
      • 构造方法
      • 利用 Scanner 进行字符读取
    • 3.3 OutputStream 概述
      • 方法
      • 说明
  • 4 文件操作案例

1 认识文件

  • 我们平时说的文件一般指的都是存储在硬盘上的普通文件,形如 txt,jpg,rar,mp4 等这些文件都可以认为是普通文件,它们都是在硬盘上存储的。
  • 在计算机中,文件可能是一个广义的概念,就不只是包含普通文件,还可以包含 文件夹(目录文件)。
  • 操作系统中,还会使用文件来描述一些其他的硬件设备或者软件资源。例如:网卡~ 显示器~ 键盘~(操作系统中就把网卡这样的硬件设备也抽象成了一个文件)

1.1 文件存储在硬盘

普通文件是保存在硬盘上的

硬盘的基本构造:

  1. 盘片,存储数据的介质
  2. 磁头
    Java文件操作 —— IO_第1张图片
    机械磁盘通过供电,盘片就会高速运转,磁头就会在盘片上找到对应数据。

但是受限于机械硬盘的硬件结构,盘片的转速不可能无限高,机械硬盘的读写速度难以再提升更快的速度,只能在容量扩充方向发展。

固态硬盘(SSD):
Java文件操作 —— IO_第2张图片
固态硬盘的读写速度要比机械硬盘高很多。

1.2 文件的分类

文件主要分成两类

  1. 二进制文件:存储的是字节
    eg:.jpg,.exe,.class,.zip
  2. 文本文件:存储的是字符
    eg:.txt,.c,.java
    文本文件本质上也是存字节的,因为字符也是由字节构成的,只是在文本文件中,相邻的字节在一起正好能构成一个个字符。

补充小知识:

  • 判定一个文件是文本文件还是二进制文件的简单方法:将文件用 “记事本” 打开,如果打开之后是 “乱码”,就是 二进制文件的,不乱码,就是 文本文件。
  • office 系列的文件一般都是 “二进制文件”~,因为像 word 这种软件保存的不是一个单纯的 “文本”,而是一个 “富文本”,文本中带有各种格式化的信息。

1.3 关于目录结构

计算机中,保存和管理文件,是通过操作系统中的 “文件系统” 这样的一个模块来负责的。
文件系统中,一般是通过 “树形” 结构来组织磁盘上的目录和文件的

Java文件操作 —— IO_第3张图片
整体的文件系统,就是如上图这样一种树形的结构,如果是一个普通文件,就是树的叶子节点;如果是一个目录文件,目录文件就可以包含子树,这个目录就是非叶子节点;每个目录文件中可以包含多个文件,所以称为一个 N叉 树。

1.4 文件路径

如何在文件系统中如何定位我们的一个唯一的文件就成为当前要解决的问题,在操作系统中,通过了一个 “路径” 这样的概念,来描述一个具体文件 / 目录的位置

路径分为两种:

  1. 绝对路径 — 以盘符开头
    从树型结构的角度来看,树中的每个结点都可以被一条从根开始,一直到达的结点的路径所描述,而这种描述方式就被称为文件的绝对路径(absolute path)
    在这里插入图片描述
    在这里插入图片描述

  2. 相对路径 — 以 . 或 . . 开头
    除了可以从根开始进行路径的描述,我们可以从任意结点出发,进行路径的描述,而这种描述方式就被称为相对路径(relative path),相对于当前所在结点的一条路径。
    在这里插入图片描述

2 Java 中操作文件

在 Java 中提供了一个 java.io.File 类,来对一个文件(或目录)进行抽象的描述。Java 中操作文件,主要是包含两类操作:

  1. 文件系统相关的操作
  2. 文件内容相关的操作

注意,有 File 对象,并不代表真实存在的文件。

2.1 File 对象构造方法

File 的构造方法,能够传入一个路径,来指定一个文件。这个路径可以是绝对路径也可以是相对路径。

方法 说明
File(File parent, String child) 根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname) 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, String child) 根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示
    public static void main(String[] args) {
        File pFile = new File("d:\\"); // 父文件夹目录地址
        // 构造方法 1
        File file1 = new File(pFile, "a.txt");// 在父文件夹中创建一个子类File对象
        System.out.println(file1.getPath());
        // 构造方法 2
        File file2 = new File("d:\\b.txt"); // 绝对路径下构造一个File对象
        System.out.println(file2.getPath());
        // 构造方法 3
        File file3 = new File("d:\\", "c.txt"); // 第一个参数是父文件夹目录,第二个参数是File对象文件
        System.out.println(file3.getPath());
    }

Java文件操作 —— IO_第4张图片

2.2 方法

修饰符及返回值类型 方法 说明
String getParent() 返回 File 对象的父目录文件路径
String getName() 返回 FIle 对象的纯文件名称
String getPath() 返回 File 对象的文件路径
String getAbsolutePath() 返回 File 对象的绝对路径
String getCanonicalPath() 返回 File 对象的修饰过的绝对路径
boolean exists() 判断 File 对象描述的文件是否真实存在
boolean isDirectory() 判断 File 对象代表的文件是否是一个目录
boolean isFile() 判断 File 对象代表的文件是否是一个普通文件
boolean createNewFile() 根据 File 对象,自动创建一个空文件。成功创建后返回 true
boolean delete() 根据 File 对象,删除该文件。成功删除后返回 true
void deleteOnExit() 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行
String[] list() 返回 File 对象代表的目录下的所有文件名
File[] listFiles() 返回 File 对象代表的目录下的所有文件,以 File 对象表示
boolean mkdir() 创建 File 对象代表的目录
boolean mkdirs() 创建 File 对象代表的目录,如果必要,会创建中间目录
boolean renameTo(File dest) 进行文件改名,也可以视为我们平时的剪切、粘贴操作
boolean canRead() 判断用户是否对文件有可读权限
boolean canWrite() 判断用户是否对文件有可写权限

2.2.1 代码示例

get 系列方法

public static void main(String[] args) throws IOException {
        File f = new File("d:\\test.txt");
        boolean fTxt = f.createNewFile();
        System.out.println(f.getParent()); // 获取到文件的父目录
        System.out.println(f.getName());    // 获取到文件名
        System.out.println(f.getPath());    // 获取到文件路径(构造 File 的时候指定的路径)
        System.out.println(f.getAbsolutePath()); // 获取到绝对路径
        System.out.println(f.getCanonicalPath()); // 获取到标准路径

        System.out.println("=================");

        File f2 = new File("./test.txt");
        boolean f2Txt = f2.createNewFile();
        System.out.println(f2.getParent()); // 获取到文件的父目录
        System.out.println(f2.getName());    // 获取到文件名
        System.out.println(f2.getPath());    // 获取到文件路径(构造 File 的时候指定的路径)
        System.out.println(f2.getAbsolutePath()); // 获取到绝对路径
        System.out.println(f2.getCanonicalPath()); // 获取到标准路径
    }

Java文件操作 —— IO_第5张图片
Java文件操作 —— IO_第6张图片

普通文件的创建

    public static void main(String[] args) throws IOException {
        File file = new File("./a.txt"); // 新创建一个 File 对象
        System.out.println("文件是否存在:"+file.exists());// 判断文件是否存在
        System.out.println("是否是一个目录:"+file.isDirectory()); // 判断 File 对象代表的文件是否是一个目录
        System.out.println("是否是一个普通文件:"+file.isFile()); // 判断 File 对象代表的文件是否是一个普通文件
        System.out.println("创建文件:" + file.createNewFile()); // 根据 File 对象,自动创建一个空文件。成功创建后返回 true
        System.out.println("文件是否存在:"+file.exists());
        System.out.println("是否是一个目录:"+file.isDirectory());
        System.out.println("是否是一个普通文件:"+file.isFile());
        System.out.println("再次创建:"+file.createNewFile());
    }

Java文件操作 —— IO_第7张图片

普通文件的删除

    public static void main(String[] args) throws IOException, IOException {
        File file = new File("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());
    }

Java文件操作 —— IO_第8张图片

deleteOnExit

    public static void main(String[] args) throws IOException, IOException {
        File file = new File("some-file.txt");	// 要求该文件不存在,才能看到相同的现象
        System.out.println("文件是否存在:"+file.exists());
        System.out.println(file.createNewFile());
        System.out.println("文件是否存在:"+file.exists());
        file.deleteOnExit();
        System.out.println("文件是否存在:"+file.exists());
    }

Java文件操作 —— IO_第9张图片

创建文件夹(目录)

    public static void main(String[] args) throws IOException {
        File dir = new File("./dir");	// 要求该目录不存在,才能看到相同的现象
        System.out.println("是否是一个目录:"+dir.isDirectory());
        System.out.println("是否是一个普通文件:"+dir.isFile());
        System.out.println("创建文件夹"+dir.mkdir());
        System.out.println("是否是一个目录:"+dir.isDirectory());
        System.out.println("是否是一个普通文件:"+dir.isFile());
    }

Java文件操作 —— IO_第10张图片

mkdirs()

mkdir() 的时候,如果中间目录不存在,则无法创建成功; mkdirs() 可以解决这个问题。

文件重命名

    public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");	// 要求 test.txt 得存在,可以是普通文件,可以是目录
        File dest = new File("./dest.txt");   // 要求 dest.txt 不存在
        System.out.println("文件1是否存在:"+file.exists());
        System.out.println("文件2是否存在:"+dest.exists());
        System.out.println("重命名:"+file.renameTo(dest));
        System.out.println("文件1是否存在:"+file.exists());
        System.out.println("文件2是否存在:"+dest.exists());
    }

Java文件操作 —— IO_第11张图片

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

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

  1. 字节流对象:针对二进制文件,是以字节为单位进行读写的。
  2. 字符流对象:针对文本文件,是以字符为单位进行读写的。

Java文件操作 —— IO_第12张图片

3.1 InputStream 概述

方法

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

Java文件操作 —— IO_第13张图片

read 提供了三个版本的重载:

  1. 无参数版本:一次读一个字节,返回值是读到的这个字节
  2. 一个参数版本:一次读若干字节,把读到的结果放到参数中指定的数组中,返回值就是读到的字节数
  3. 三个参数的版本:一次读若干个字节,把读到的结果放到参数中指定的数组中,返回值就是读到的字节数(从 off 这个下标的位置开始,len 表示最多能放多少个字节)

说明

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

3.2 FileInputStream 概述

构造方法

构造方法 说明
FileInputStream(File file) 利用 File 构造文件输入流
FileInputStream(String name) 利用文件路径构造文件输入流
    public static void main(String[] args) {
        // 构造方法中需要指定打开文件的路径
        try (InputStream inputStream = new FileInputStream("a.txt")){ // 使用 try with resources
            // 一次读取若干个字节
            while(true) {
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);
                if(len == -1) {
                    // 如果返回 -1 说明读取完了
                    break;
                }
                for (int i = 0; i < len; i++) {
                    System.out.println(buffer[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Java文件操作 —— IO_第14张图片

利用 Scanner 进行字符读取

构造方法 说明
Scanner(InputStream is, String charset) 使用 charset 字符集进行 is 的扫描读取
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("a.txt")) {
           try (Scanner scanner = new Scanner(is, "UTF-8")) {
               while (scanner.hasNext()) {
                   String s = scanner.next();
                   System.out.print(s);
               }
           }
        }
    }

3.3 OutputStream 概述

方法

修饰符及返回值类型 方法签名 说明
void write(int b) 写入要给字节的数据
void write(byte[] b) 将 b 这个字符数组中的数据全部写入 os 中
int write(byte[] b, int off, int len) 将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
void close() 关闭字节流
void flush() 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

说明

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream

    public static void main(String[] args) {
        // 每次按照写方式打开文件,都会清空原有文件的内容,再从起始位置开始往后写
        try (OutputStream outputStream = new FileOutputStream("a.txt")){
            byte[] buffer = new byte[]{97,98,99};
            outputStream.write(buffer);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

4 文件操作案例

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

    public static void main(String[] args) throws IOException {
        // 1. 输入要扫描的文件路径
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径: ");
        String rootDirPath = scanner.next();
        System.out.println("请输入要查询的关键词: ");
        String word = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("输入的路径非法!");
            return;
        }
        // 2. 递归的进行遍历
        scanDir(rootDir, word);
    }

    private static void scanDir(File rootDir, String word) throws IOException {
        // 1. 先列出 rootDir 中都有哪些内容
        File[] files = rootDir.listFiles();
        if (files == null) {
            return;
        }
        // 2. 遍历每个元素, 针对普通文件和目录分别进行处理.
        for (File f : files) {
            if (f.isFile()) {
                // 针对文件进行内容查找
                if (containsWord(f, word)) {
                    System.out.println(f.getCanonicalPath());
                }
            } else if (f.isDirectory()) {
                // 针对目录进行递归
                scanDir(f, word);
            }
        }
    }

    private static boolean containsWord(File f, String word) {
        // 写代码, 慎重使用缩写!!! 缩写的可读性会比较差. (一些业界常见缩写, 可以用, 不要随便滥用)
        StringBuilder stringBuilder = new StringBuilder();
        // 把 f 中的内容都读出来, 放到一个 StringBuilder 中
        try (Reader reader = new FileReader(f)) {
            char[] buffer = new char[1024];
            while (true) {
                int len = reader.read(buffer);
                if (len == -1) {
                    break;
                }
                // 把这一段读到的结果, 放到 StringBuilder 中
                stringBuilder.append(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // indexOf 返回的是子串的下标. 如果 word 在 stringBuilder 中不存在, 则返回下标为 -1
        return stringBuilder.indexOf(word) != -1;
    }

你可能感兴趣的:(笔记,Java,java,文件操作IO)