【Java EE】-文件IO

作者:学Java的冬瓜
博客主页:☀冬瓜的主页
专栏:【JavaEE】
分享
主要内容:文件的认识,绝对路径相对路径,二进制文件文本文件,File的方法的使用,普通文件的创建删除,目录的创建,文件的重命名。InputStream和OutputStream读写文件,Reader和Writer读写文件。try with resources、字节流读写文件,字符流读写文件。字节流Scanner读文件。查找并删除包含指定字符的文件、复制文件、查找包含关键字符文件。

文章目录

  • 一、文件
    • 1、文件的基本认识
    • 2、绝对路径和相对路径
    • 3、文件的类型
  • 二、File类
    • 1、概述和方法列表
    • 2、方法的使用
      • @ 使用get系列方法
      • @ 普通文件的创建和删除
      • @ 目录的创建和删除
      • @ 文件的重命名
  • 三、流操作文件
    • 1、InputStream && OutputStream
      • @ InputStream
      • @ OutputStream
    • 2、Reader && Writer
      • @ Reader
      • @ Writer
  • 四、小练习
    • 1、查找/删除文件名包含关键字的文件
    • 2、复制普通文件
    • 3、查找包含关键字符文件

一、文件

1、文件的基本认识

狭义的文件:硬盘上的目录和文件。

广义的文件:泛指计算机中的很多软硬件资源。在操作系统中,把很多的硬件设备和软件设备资源抽象成了文件按照文件的方式来统一管理。比如在后面网络编程中的网卡。

2、绝对路径和相对路径

每个文件在硬盘上都有一个具体的路径,比如:D:\work\path\test.txt。这个路径使用 \ 或者 / 作为分隔符,这两种分隔符作用一样。只是使用反斜杠时,可能会有转义的问题,这时候就需要用 \\ 表示 \。因此,更推荐正斜杠 /的写法。
盘符:我们电脑上的C、D、E盘都是盘符。其实A、B盘就是软盘,但是它们空间太小,只有几个MB,现在都不用了。

绝对路径:像上面那样带有盘符的文件具体的路径就是绝对路径,如使用正斜杠表示上述文件:D:/work/path/test.txt
相对路径:以工作目录作为基准,以 .(当前目录) 或者 ..(上级目录) 开头(.有时候可以省略),找到文件的路径。比如:上述例子,如果工作路径是D:/work/path,那test.txt的相对路劲为:./test.txt;而D:/work的相对路径就是:../,D盘的路径为:../../

注意:在linux中没有盘符的概念,所以使用terminal(终端)操作时,直接使用cd切换文件就行。但是在windows中,要先且换盘符,才能使用cd切换到该盘符中的文件。
切换盘符:
在这里插入图片描述
切换到该盘符下的的work目录下的Linux目录:可以使用相对路径,也可使用绝对路径。(此时工作目录就是D盘)
在这里插入图片描述

3、文件的类型

文件类型包括:文本文件和二进制文件。文本文件和二进制文件就包括了视频、声音、图片、word、源代码等等文件。

二进制文件:

二、File类

1、概述和方法列表

对文件的操作主要包括以下两点:
1>针对文件系统操作(文件创建、删除...) => 使用File提供的方法
2>针对文件内容操作(文件的读和写) => 通过流实现

下面我们就来使用File提供的方法,对文件进行创建、删除、重命名等操作。

返回值类型 方法名 说明
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(Filedest) 进行文件改名,也可以视为我们平时的剪切、粘贴操作
boolean canRead() 判断用户是否对文件有可读权限
boolean canWrite() 判断用户是否对文件有可写权限

2、方法的使用

首先目录结构:
【Java EE】-文件IO_第1张图片

@ 使用get系列方法

// 使用get系列方法
public class FileDemo1 {
    public static void main(String[] args) throws IOException {
        File file = new File("./testTxt/test.txt");
        System.out.println(file.getName());  // 获取当前文件的文件名
        System.out.println(file.getParent());  // 当前文件所在目录的路径
        System.out.println(file.getPath());    // 当前文件的路径,可以是相对路径可以是绝对路径,使用什么new File,就使用什么路径。
        System.out.println(file.getAbsolutePath());   // 获取当前文件的绝对路径
        System.out.println(file.getCanonicalPath());  // 把绝对路径"化简"后得到这个路径,去掉了".\"
    }
}

结果:

test.txt
.\testTxt
.\testTxt\test.txt
D:\work\JavaWeb\java-web\IO-20230404-FileFunc\.\testTxt\test.txt
D:\work\JavaWeb\java-web\IO-20230404-FileFunc\testTxt\test.txt

@ 普通文件的创建和删除

  • 在这里删除不做演示了,直接使用file.delete()即可。
  • createnewfile只能在项目根目录下创建文件,无法通过相对路径指定包内创建文件。如果还是想创建,那得使用File类其它构造方法创建File对象。
  • delectOnExit() 则是退出进程时删除,主要用于删除临时文件,比如编辑word文档时会默认有一个临时文件(你要把隐藏文件显示才能在桌面上看到),当你把这个word × 了后,这个进程就结束了,所以临时文件就会删除。
    那么为啥要有这个临时文件?就是为了方便,防止写了一大堆后,你没有手动保存,突然断电了,你的进程未退出,然后再打开时,你写的东西全部消失,这是一件非常伤脑筋的事情,因此才使用临时文件,然后退出进程时删除。
// 普通文件的创建
public class FileDemo2 {
    public static void main(String[] args) throws IOException {
        // File file = new File("./testTxt/test.txt");
        File file = new File("D:/work/test.txt");
        System.out.println(file.exists());        // file是否存在
        System.out.println(file.isDirectory());   // file是否是目录
        System.out.println(file.isFile());        // file是否是文件
        System.out.println(file.createNewFile()); // 文件不存在则创建一个文件,返回true;文件已经存在,就不创建文件,返回false。
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());
    }
}

结果:

false
false
false
true
true
false
true

@ 目录的创建和删除

创建一级目录:mkdir()
创建多级目录:mkdirs()
删除目录:file.delete(),创建多级目录后使用file.delete是删除最深处的目录。

@ 文件的重命名

// 文件的重命名
public class FileDemo4 {
    public static void main(String[] args) {
        File file = new File("./testTxt");
        File dest = new File("./test");
        file.renameTo(dest);
    }
}

三、流操作文件

1、InputStream && OutputStream

InputStreamOutputStream都是抽象类,想要使用就得需要实现类。它们的是实现类有很多,我们这里以FileInputStreamFileOutputStream为例。

@ InputStream

InputStream的方法:

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

注意:使用FileInputStream创建带有流的文件对象时,可以传一个文件File,也可以传文件File的路径。

在下面示例中,我们读取了D:/work/test.txt文件,文件中若有汉字时,使用十六进制打印,然后查看码表。码表网站:查看码表网站

字节流读文件法一:手动打开关闭资源+read():

//第一种读文件: 手动打开资源,关闭资源的方式+read()
    public static void main(String[] args) throws IOException {
        // 注意:打开文件 关闭文件
        //    一定要记得关闭资源
        InputStream inputStream = new FileInputStream("D:/work/test.txt");
        while (true){
            int b = inputStream.read();
            if (b == -1){   // 注意:当从硬盘中的文件读到内存中的数据读完时,b=-1。
                break;      //      b作为bite的表示范围是:0~255(-128~127)
                            //      此时就是还需要表示-1,所以使用int,就可以满足-1~255的要求
            }

            // 注意:走到这里说明文件还在读
            System.out.println((byte)b);
            // 如果是汉字,可以使用十六进制打印,然后查码表
            // 注意:查看码表网站:http://mytju.com/classcode/tools/encode_utf8.asp
            // System.out.printf("%x\n",(byte)b);
        }
        inputStream.close();
    }

字节流读文件法二:try with resources + read(byte[] b)

// 第二种读文件:利用try() 自动关闭资源(更推荐使用)+ 使用字节缓冲区(read(byte[] b))
//              这种方式关闭资源更方便,效率也更高。
    public static void main2(String[] args) throws FileNotFoundException {
        // 注意:把资源放在 try()的括号里,那么当代码执行完try,资源就可以自己释放。
        try (InputStream inputStream = new FileInputStream("D:/work/test.txt");){
            while (true){
                // 1、把文件按照字节的方式放入buffer
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);  //注意:read方法读取文件数据放入buffer,返回buffer的长度len
                if(len == -1){
                    break;
                }

                // 2、放入buffer的字节打印,再循环放入,再打印,直到len=-1,表示文件读取结束。
                for (int i = 0; i < len; i++) {
                    System.out.printf("%x\n",buffer[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

字节流读文件法三:try with resources + Scanner)

	public static void main(String[] args) {
        // 这里参数in就是一个InputStream流,用来从键盘输入,读到内存中。
        // Scanner scanner = new Scanner(System.in);
        // Scanner的close操作本质上是关闭传入的InputStream这个流对象,在下面中流对象我们使用try with resource关闭了。

        try(InputStream inputStream = new FileInputStream("D:/work/test.txt")) {
            // 注意1:这里把inputStream传入Scanner,就是从文件中读取了字节数据
            Scanner scanner = new Scanner(inputStream);

            // 注意2:这样操作后:就可以使用Scanner类下的next等众多方法,很方便。
            System.out.println(scanner.nextLine());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

@ OutputStream

OutputStream的方法:

返回值类型 方法名 说明
void write(int b) 写入要给字节的数据
void write(byte[] b) 将 b 这个字符数组中的数据全部写入 OutputStream中
int write(byte[] b, int off,int len) 将 b 这个字符数组中从 off 开始的数据
写入 OutputStream中,一共写 len 个
void close() 关闭字节流
void flush() 冲刷/刷新

注意:

  • 对flush方法的解释:我们都知道,IO操作是从硬盘或者其它地方读写数据,相对于内存来说是很慢的。因此为了减少IO次数,大多数的OutputStream在写数据时,会将数据先写入一个内存的一个空间——缓冲区。等缓冲区满了(或者其他条件),再将数据写入到设备中。因此有可能存在我们写的数据有一部分留在缓冲区,需要我们冲刷,然后写入设备,这个操作就是 flash()方法。
  • OutputStream.write()的操作:先检查有没有这个file文件,如果没有当前指定,则创建文件,再写入数据;如果有当前指定文件,则先清除原数据,再将新数据写入文件。(Writer写文件同理)

字节流写文件:try with resources + write() + flash()

// 字节流写文件:try with resources + write();对应InputStream的OutputStream写文件的法二可以自己尝试去写,即把字节数组写入文件。
	public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("D:/work/test.txt")){
            // 注意:OutputStream.write()和Reader.write()都一样。
            //      这种写的操作时:先找文件,如果没有这个文件,就创建文件再写入文件
            //      如果有文件,就先清除原来内容,再把新内容写入
            //     InputStream.read()和Reader.read都是先查文件,无这个文件,则抛出异常,有文件再去查找。
            outputStream.write('A');
            outputStream.write('B');
            outputStream.write('C');
            outputStream.write('D');

            outputStream.flush(); // 刷新\冲刷。一般close时会带有flash操作;也可以手动写,防止未写入文件的情况
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2、Reader && Writer

ReaderWriter都是抽象类,想要使用就得需要实现类。它们的是实现类有很多,我们这里以FileReaderFileWriter为例。

@ Reader

字符流读文件法一:try with resources + Reader + FileReader

	public static void main1(String[] args) {
        try (Reader reader = new FileReader("D:/work/test.txt")){
            while (true){
                int r = reader.read();
                if(r == -1){
                    break;
                }
                System.out.println(r);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

字符流读文件法二:try with resources + BufferedReader + FileReader

// 使用字符缓冲区读取文件
    public static void main(String[] args) {
        // 注意:把FileReader的对象传给字符缓冲区BufferReader
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/work/test.txt"))){
            String line;
            // 注意:当读完文件时,line就是等于null
            while ((line = bufferedReader.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

@ Writer

字符流写文件:try with resources + Writer.write() + flash()

	public static void main(String[] args) {
        try(Writer writer = new FileWriter("D:/work/test.txt")) {
            writer.write("你好,ABCD");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } ;
    }

四、小练习

1、查找/删除文件名包含关键字的文件

需求:根据指定目录,该目录可能包含多个子目录和多个文件,要查询该目录下的名字中包含关键字的普通文件,并可以选择删除与否该普通文件。

package practice;

import java.io.File;
import java.util.Scanner;

// 在指定根目录下查找并删除名字带有关键字的文件
public class SearchAndDeleteFile {
    // 为了递归时输入方便,scanner直接定义成类变量
    private static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        // 用户输入一个指定目录,在当前目录下查找要删除文件
        System.out.println("请输入要扫描的根目录(绝对路径 OR 相对路径):");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);
        if(!rootDir.isDirectory()){
            System.out.println("您输入的根目录不存在或者不是目录,退出!");
            return;
        }

        // 用户输入要删除的文件名关键字
        System.out.println("请输入您要删除文件名的关键字符:");
        String keyNameToDelete = scanner.next();

        // 使用深度优先遍历,查找rootDirPath根目录下的文件或者子目录中有无要删除文件名关键字,有则输入y or Y确认删除
        deleteDir(rootDir, keyNameToDelete);
    }

    // 删除文件
    private static void deleteDir(File rootDir, String keyName) {
        System.out.println("curFile:" + rootDir.getAbsolutePath());  // 查看递归具体当前路径
        File[] files = rootDir.listFiles();
        if (files == null){
            // 当前目录下无文件,之间返回
            return;
        }

        for (File f : files) {
            if(f.isDirectory()){
                // f是目录,那就进一步递归,查找普通文件
                deleteDir(f, keyName);
            }else {
                // f不是目录了,而是文件了,那就在当前f的同级目录下文件当中查找相应的要删除文件
                // 找不到删除文件,就归到上一层调用,然后在这上一层中判断下一个f是目录还是文件,循环操作
                if (f.getName().contains(keyName)){
                    System.out.println("确认删除" + f.getAbsolutePath() + "吗? y or Y(确认),任意键取消:");
                    String choice = scanner.next();
                    if(choice.equals("y") || choice.equals("Y")){
                        f.delete();
                        System.out.println("删除成功!");
                    }else {
                        System.out.println("删除取消!");
                    }
                }
            }
        }
    }
}

2、复制普通文件

需求:复制普通文件,如果源文件不存在则报错;如果目标文件存在且是目录则报错,目标文件是已存在的普通文件则询问是否覆写,目标文件不存在则创建目标普通文件且复制。

package practice;

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

// 复制文件
public class CopyFile {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要复制的源文件路径:");
        String srcPath = scanner.next();
        File srcFile = new File(srcPath);
        if (!srcFile.exists() || !srcFile.isFile()){
            // 如果当前路径文件不存在或者不是普通文件,那就不复制,直接返回。
            System.out.println("源文件不存在或者不是普通文件,退出!");
            return;
        }

        System.out.println("请输入你要复制到的目标文件路径:");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        if (destFile.exists()){
            if (destFile.isDirectory()){
                System.out.println("目标文件已存在且是目录,退出!");
            }else {
                System.out.println("目标普通文件已经存在,是否要覆写? y or Y(任意键取消):");
                String choice = scanner.next();
                if (!choice.toUpperCase().equals("Y")){
                    System.out.println("停止复制,退出!");
                }
            }
        }

        // 走到这里代表源文件是一个普通文件,且目标文件不存在或者是一个将要覆写的普通文件
        //                              如果不存在就创建文件再写,如果存在就直接覆写
        try(InputStream inputStream = new FileInputStream(srcPath);
            OutputStream outputStream = new FileOutputStream(destPath)) {
            while (true){
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);
                if(len == -1){
                    break;
                }
                outputStream.write(buffer);
                outputStream.flush();
            }
            System.out.println("复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3、查找包含关键字符文件

需求:扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录),放在集合中并打印。

package practice;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class SearchAllKeyFile {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的根目录(绝对路径 OR 相对路径):");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);

        if (!rootDir.isDirectory()) {
            System.out.println("您输入的根目录不存在或者不是目录,退出");
            return;
        }

        System.out.println("请输入要找出的文件名中的字符: ");
        String keyNameSearch = scanner.next();
        List<File> listResult = new ArrayList<>();
        // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历
        scanDirWithContent(rootDir, keyNameSearch, listResult);
        System.out.println("共找到了符合条件的文件 " + listResult.size() + " 个,它们分别是:");
        for (File f : listResult) {
            System.out.println(f.getCanonicalPath());
        }
    }

    private static void scanDirWithContent(File rootDir, String keyNameSearch,
                                           List<File> listResult) throws IOException {
        File[] files = rootDir.listFiles();
        if (files == null || files.length == 0){
            return;
        }

        for (File f : files) {
            if (f.isDirectory()){
                scanDirWithContent(f,keyNameSearch,listResult);
            }else {
                if (f.getName().contains(keyNameSearch)){
                    listResult.add(f.getAbsoluteFile());
                }
            }
        }
    }
}

你可能感兴趣的:(JAVAEE,java-ee,java,windows)