文件操作和IO

发布于:2024-04-27 ⋅ 阅读:(24) ⋅ 点赞:(0)


前言

文件操作与IO(Input/Output)是计算机编程中非常重要的一个部分,它涉及到如何读取和写入数据到计算机的文件系统中。无论是读取用户输入,还是将数据存储在文件中,文件操作和IO操作都是必不可少的。


一、文件操作

1.1认识文件

我们先来认识狭义上的⽂件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进⾏数据保存时,往往不是保存成⼀个整体,⽽是独⽴成⼀个个的单位进⾏保存,这个独⽴的单位就被抽象成⽂件的概 念,就类似办公桌上的⼀份份真实的⽂件⼀般。

1.2硬盘上的文件目录结构

随着⽂件越来越多,对⽂件的系统管理也被提上了⽇程,如何进⾏⽂件的组织呢,⼀种合乎⾃然的想法出现了,就是按照层级结构进⾏组织⸺也就是我们数据结构中学习过的树形结构。这样,⼀种专⻔⽤来存放管理信息的特殊⽂件诞⽣了,也就是我们平时所谓⽂件夹(folder)或者⽬录 (directory)的概念。

Windows上的树型结构组织
在这里插入图片描述
文件夹或目录中保存的是文件的元信息,包括文件名、大小、创建日期、修改日期等信息,而文件的具体内容则存储在文件中。通过文件夹或目录,用户可以方便地管理和组织文件。
在这里插入图片描述

1.3文件的路径

a)绝对路径:从树型结构的⻆度来看,树中的每个结点都可以被⼀条从根开始,⼀直到达的结点的路径所描述,⽽这种描述⽅式就被称为⽂件的绝对路径(absolute path)。
在这里插入图片描述

b)相对路径:
除了可以从根开始进⾏路径的描述,我们可以从任意结点出发,进⾏路径的描述,⽽这种描述⽅式就被称为相对路径(relative path),相对于当前所在结点的⼀条路径。
eg.如果当前工作目录是C:\Program Files(x86)\Windows NT,要引入该目录下的子目录Accessories中的文件wordpad.exe,可以使用相对路径Accessories\wordpad.exe
在这里插入图片描述
eg.如果从WindowsPowerShell出发去往Windows NT的路径。

在这里插入图片描述

在这里插入图片描述

1.4文本文件VS二进制文件

文本文件和二进制文件都是存储数据的文件,它们之间的主要区别在于存储数据的方式和数据处理方式。

  • 文本文件:
    文本文件存储的数据是由字符组成的,每个字符都有特定的编码(通常是ASCII码或Unicode码)表示。
    文本文件可以直接打开并查看内容,因为数据是以可读形式存储的。
    文本文件可以使用文本编辑器(如记事本、Word等)进行编辑和修改。
    文本文件通常用于存储文本信息,如文档、代码等。
  • 二进制文件:
    二进制文件存储的数据以二进制形式编码,没有直接可读性。
    二进制文件不能直接使用文本编辑器查看,需要特定的应用程序对其进行处理。
    二进制文件通常用于存储非文本信息,如图像、音频、视频、可执行程序等。
    二进制文件可以存储大量的数据,因为它们不受字符编码的限制。
    综上所述,文本文件和二进制文件的主要区别在于存储数据的方式和数据处理方式,而不同类型的文件适用于不同的数据存储需求和处理需求。

二、文件系统操作

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

2.1File概述

属性:
在这里插入图片描述
构造方法:
在这里插入图片描述
方法:
在这里插入图片描述

2.2代码示例

  • 观察 get 系列的特点和差异
public class IODemo1 {
    public static void main(String[] args) throws IOException {
        //File f=new File("E://数字图像处理//代码//Demo1/maomao.jpg");
        File f=new File("./maomao.jpg");
        System.out.println(f.getParent());
        System.out.println(f.getName());
        System.out.println(f.getPath());
        System.out.println(f.getAbsoluteFile());
        System.out.println(f.getCanonicalFile());
    }
}

在这里插入图片描述

  • 普通⽂件的创建、删除
public class IODemo2 {
    public static void main(String[] args) throws IOException {
        File file=new File("./maomao.jpg");
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());
        boolean ret=file.createNewFile();
        System.out.println("ret="+ret);
    }
}

在这里插入图片描述

  • 普通⽂件的删除
public class IODemo3 {
    public static void main(String[] args) throws InterruptedException {
        File file=new File("./maomao.jpg");
        boolean ret=file.delete();
        System.out.println(ret);

        /*file.deleteOnExit();   退出之后再删除,这种文件称为“临时文件”
        Thread.sleep(3000);*/

    }
}

在这里插入图片描述

  • 打印当前目录下包含的的目录文件
public class IODemo4 {
    public static void main(String[] args) {
        File file=new File(".");//打印当前目录下包含的的目录文件,.就表示io这个文件
        String[] files= file.list();
        System.out.println(Arrays.toString(files));
    }
}

在这里插入图片描述

  • 观察⽬录创建
public class IODemo5 {
    public static void main(String[] args) {
        File file=new File("./aaa");
        boolean ret=file.mkdir();//mkdir创建单个目录
        //File file=new File("./aaa/bbb/ccc");
        //boolean ret=file.mkdirs();//mkdirs创建多级目录
        System.out.println(ret);

    }
}

在这里插入图片描述
在这里插入图片描述

  • 观察⽂件重命名
public class IODemo6 {
    public static void main(String[] args) throws IOException {
        File file=new File("./test2.txt");
        file.createNewFile();
        File src=new File("./test.txt");
        File desc=new File("./test2.txt");//renameTo重命名文件
        //File desc=new File(".aaa/test2.txt");//renameTo移动文件位置
        src.renameTo(desc);
        file.delete();
    }
}

三、文件内容操作–文件流

3.1字节流

字节流—每次读写的最小单位就是一个字节。
(InputStream、OutputStream)

FileInputStream 概述
构造方法:
在这里插入图片描述
读文件

public class Main {
        public static void main(String[] args) throws IOException {
            try (InputStream is = new FileInputStream("hello.txt")) {
                byte[] buf = new byte[1024];
                int len;

                while (true) {
                    len = ((FileInputStream) is).read(buf);
                    if (len == -1) {
                        // 代表⽂件已经全部读完
                        break;
                    }

                    for (int i = 0; i < len; i++) {
                        System.out.printf("%c", buf[i]);
                    }
                }
            }
        }
    }

OutputStream 概述
方法:

在这里插入图片描述
利⽤ OutputStreamWriter 进⾏字符写⼊

public class Main {
        public static void main(String[] args) throws IOException {
            try (OutputStream os = new FileOutputStream("output.txt")) {
                String s = "Nothing";
                byte[] b = s.getBytes();
                //String s = "你好中国";
 				//byte[] b = s.getBytes("utf-8");
                os.write(b);
                // 不要忘记 flush
                os.flush();
            }
        }
    }

如何按字节进⾏数据读

try (
    InputStream is = ...) {
        byte[] buf = new byte[1024];
        while (true) {
            int n = is.read(buf);
            if (n == -1) {
                break;
            }
            // buf 的 [0, n) 表⽰读到的数据,按业务进⾏处理
        }
    }

如何按字节进⾏数据写

try (OutputStream os = ...) {
        byte[] buf = new byte[1024];
        while (/* 还有未完成的业务数据 */) {
            // 将业务数据填⼊ buf 中,⻓度为 n
            int n = ...;
            os.write(buf, 0, n);
        }
        os.flush(); // 进⾏数据刷新操作
    }

3.2字符流

字符流—每次读写的最小单位是一个字符(一个字符可能由多个字节构成),字符流可以内部处理字符编码。
(Reader、Writer)
PrintWriter 类中提供了我们熟悉的 print/println/printf ⽅法

public class Main {
        public static void main(String[] args) throws IOException {
            try (OutputStream os = new FileOutputStream("output.txt")) {
                try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-8
 try (PrintWriter writer = new PrintWriter(osWriter)) {
                    writer.println("我是第⼀⾏");
                    writer.print("我的第⼆⾏\r\n");
                    writer.printf("%d: 我的第三⾏\r\n", 1 + 1);
                    writer.flush();
                }
            }
        }
    }
}

3.3练习

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

package io;

import java.io.*;
import java.util.Scanner;

public class IODemo15 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要搜索的路径: ");
        String rootPath = scanner.next();
        System.out.println("请输入要查询的词: ");
        String word = scanner.next();

        File rootFile = new File(rootPath);
        if (!rootFile.isDirectory()) {
            System.out.println("输入的要搜索的路径不正确!");
            return;
        }

        scanDir(rootFile, word);
    }

    private static void scanDir(File rootFile, String word) {
        File[] files = rootFile.listFiles();
        if (files == null) {
            return;
        }
        for (File f : files) {
            System.out.println("当前遍历到: " + f.getAbsolutePath());
            if (f.isFile()) {
                // 在文件内容中搜索
                searchInFile(f, word);
            } else if (f.isDirectory()) {
                // 递归遍历
                scanDir(f, word);
            } else {
                // 暂时不需要
                ;
            }
        }
    }

    private static void searchInFile(File f, String word) {
        // 通过这个方法在文件内部进行搜索
        // 1. 把文件内容都读取出来.
        try (InputStream inputStream = new FileInputStream(f)) {
            StringBuilder stringBuilder = new StringBuilder();
            while (true) {
                byte[] buffer = new byte[1024];
                int n = inputStream.read(buffer);
                if (n == -1) {
                    break;
                }
                // 此处只是读取出文件的一部分. 需要把文件内容整体拼接在一起.
                String s = new String(buffer, 0, n);
                stringBuilder.append(s);
            }

            // 加了打印之后, 可以看到, 文件内容是对的. 说明后面的匹配有问题.
            System.out.println("[debug] 文件内容: " + stringBuilder);

            // 当文件读取完毕, 循环结束之后, 此时 stringBuilder 就是包含文件整个内容的字符串了.
            if (stringBuilder.indexOf(word) == -1) {
                // 没找到要返回.
                return;
            }
            // 找到了, 打印文件的路径
            System.out.println("找到了! " + word + " 存在于 " + f.getAbsolutePath());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

最后,码字不易,如果觉得对你有帮助的话请点个赞吧,关注我,一起学习,一起进步!