目录

【JavaEE】文件操作和IO

【JavaEE】文件操作和IO

  • 一、认识文件
      • [1.1 狭义和广义的文件概念](#1.1 狭义和广义的文件概念)
      • [1.2 文件路径](#1.2 文件路径)
      • [1.3 文件的分类](#1.3 文件的分类)
  • [二、Java 中操作⽂件](#二、Java 中操作⽂件)
      • [2.1 File类](#2.1 File类)
      • [2.2 代码演示](#2.2 代码演示)
  • [三、文件内容的读写 ------ 数据流](#三、文件内容的读写 —— 数据流)
      • [3.1 字节流和字符流](#3.1 字节流和字符流)
      • [3.2 特别注意](#3.2 特别注意)
  • 四、实战演示
      • [4.1 查找删除文件](#4.1 查找删除文件)
      • [4.2 普通文件的复制](#4.2 普通文件的复制)
      • [4.3 文件内容查找](#4.3 文件内容查找)

博客结尾附有此篇博客的全部代码!!!

一、认识文件

1.1 狭义和广义的文件概念

  • 狭义的文件:通常指存储在磁盘中的普通文件,是由字节(或字符)序列构成的数据集合,以一定的结构保存在存储介质中(如 FAT、ext4 等文件系统)。
  • 广义的文件:概念则更为广泛,不仅包括磁盘上的普通文件,还涵盖了其他多种资源。例如:
    1.设备文件:包括字符设备和块设备,代表硬件设备(如键盘、显示器、硬盘等)
    2.目录:存储文件名和相关元数据的特殊文件
    3.虚拟文件:如 Linux 系统中 /proc 下的伪文件,用于显示内核和进程状态。
    4.其他资源:在某些情况下,网卡、声卡等外设也可以被视为文件

1.2 文件路径

绝对路径 :绝对路径是从文件系统的根目录(通常是 / 或 C:\)开始的完整路径,它唯一地标识了文件或目录的位置。
相对路径:相对路径是从当前工作目录(Current Working Directory,CWD)开始的路径,它描述了从当前目录到目标文件或目录的相对位置。

1.3 文件的分类

文本文件(以记事本打开可以看懂的):是由可打印字符(如字母、数字、标点符号等)组成的文件,通常以人类可读的形式存储数据。

内容表示:文件内容以字符编码(如ASCII、UTF-8、GBK 等)存储,每个字符占用一个或多个字节。
二进制文件(以记事本打开时乱码):二进制文件是以二进制形式存储数据的文件,文件内容可以是任意字节序列,不一定对应可打印字符。

内容表示:文件内容以字节序列存储,可以包含任何数值(0-255),不局限于字符编码。

二、Java 中操作⽂件

2.1 File类

Java中将文件的操作封装在File类中。(创建目录、删除文件、重命名文件等...)

属性:

构造方法:

File 的构造方法,能够传入一个路径,来指定一个文件,这个路径可以是绝对路径也可以是相对路径,构造好对象之后,就可以通过这些方法,来完成一些具体的功能了

方法:

2.2 代码演示

在Java代码编写的时候不能用windows默认的分隔符:"/"斜杠

idea中用斜杠可能会被JVM识别成转义字符

我们可以用反斜杠""或者"//"来表示。

java 复制代码
import java.io.File;
import java.io.IOException;

public class Demo1 {
    public static void main(String[] args) throws IOException {
//        File file=new File("D:Project/java/java-biog" +
//                "/Day20250312/IO测试文件.txt");
        File file = new File("./IO测试文件.txt");
        System.out.println("File 对象的⽗⽬录⽂件路径:"+file.getParent());
        System.out.println("FIle 对象的纯⽂件名称:"+file.getName());
        System.out.println("File 对象的⽂件路径:"+file.getPath());
        System.out.println("File 对象的绝对路径:"+file.getAbsolutePath());
        System.out.println("File 对象的修饰过的绝对路径:"+file.getCanonicalPath());
        System.out.println("File 对象描述的⽂件是否真实存在:"+file.exists());
        System.out.println("File 对象代表的⽂件是否是⼀个⽬录:"+file.isDirectory());
        System.out.println("File 对象代表的⽂件是否是⼀个普通⽂件:"+file.isFile());
    }
}

运行结果:

createNewFile()和delete():

java 复制代码
public class Demo2 {
    public static void main(String[] args) throws IOException {
        File file=new File("D:/Project/java/java-biog/" +
                "Day20250312/hello.txt");

        if(!file.exists()){
            boolean file1=file.createNewFile();
            System.out.println("file文件创建成功:"+file1);
        }
        System.out.println(file.delete());
    }
}



deleteOnExit():

java 复制代码
public class Demo3 {
    public static void main(String[] args) throws IOException, InterruptedException {
        File file=new File("D:/Project/java/java-biog/" +
                "Day20250312/hello.txt");
        if(!file.exists()){
            boolean file1=file.createNewFile();
            System.out.println("file文件创建成功:"+file1);
        }

        Thread.sleep(6000);
        file.deleteOnExit();
    }
}



list()和listFiles():

java 复制代码
public class Demo4 {
    public static void main(String[] args) {
        File file=new File("D:/Project/java/java-biog/" +
                "Day20250312");
        System.out.println(Arrays.toString(file.list()));
        System.out.println(Arrays.toString(file.listFiles()));
    }
}

mkdir()和mkdirs():

java 复制代码
public class Demo5 {
    public static void main(String[] args) {
        File file1=new File("D:/Project/java/java-biog/" +
                "Day20250312/hello1");
        boolean f1=file1.mkdir();
        System.out.println(f1);
        File file2=new File("D:/Project/java/java-biog/" +
                "Day20250312/hello2/aaa/bbb/ccc");
        boolean f2=file2.mkdirs();
        System.out.println(f2);
    }
}

mkdirs():

mkdir():

renameTo(File dest)、canRead()和canWrite():

java 复制代码
public class Demo6 {
    public static void main(String[] args) {
        File file1=new File("D:/Project/java/java-biog/" +
                "Day20250312/IO测试文件.txt");
        System.out.println(file1.renameTo(new File("D:/Project/java/java-biog/" +
                "Day20250312/IO.txt")));
                
        System.out.println(file1.canRead()); // 判断用户是否对文件有可读权限 false
        System.out.println(file1.canWrite()); // 判断用户是否对文件有可写权限 false
    }
}

三、文件内容的读写 ------ 数据流

文件内容的读写涉及的操作:

打开文件 --》读文件(写文件) --》关闭文件

输入:内存 --》硬盘

输出:硬盘 --》内存

3.1 字节流和字符流

字节流:主要针对二进制文本,是以字节为单位进行读写的

  • InputStream:字节输入流的超类,用于从源读取字节。
  • OutputStream:字节输出流的超类,用于向目标写入字节。

字符流:主要针对二进制文本,是以字节为单位进行读写的

  • Reader:针对文本文件,是以字符为单位进行读写的
  • Writer:字符输出流的超类,用于向目标写入字符。

以上面两个文件给大家演示:

字节流

InputStream

InputStream是一个抽象类,要使用还需要实现具体的类(FileInputStream)。

java 复制代码
public class Demo8 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis1=null;
        try{
             fis1 = new FileInputStream("D:/Project/java/java-biog/" +
                    "Day20250312/IO测试文件目录/测试图片.png");
            while(true){
                int ch = fis1.read();
                if(ch == -1){
                    break;
                }
                System.out.println(ch);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally{
            fis1.close();
        }
    }
}

改进之后,代码显得不美观!

Java 中提供了一个语法,try with resourcestry ( )

java 复制代码
public class Demo9 {
    public static void main(String[] args) throws FileNotFoundException {
        try(InputStream inputStream=new FileInputStream("D:/Project/java/java-biog/"  +
                                    "Day20250312/IO测试文件目录/测试图片.png")){
            while(true){
                int n=inputStream.read();
                if(n==-1) break;
                System.out.println(n);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
java 复制代码
public class Demo10 {
    public static void main(String[] args) throws FileNotFoundException {
        try(InputStream i=new FileInputStream("D:/Project/java/java-biog/"  +
                "Day20250312/IO测试文件目录/测试图片.png")){
            while(true){
                byte[] b=new byte[1024];
                int n=i.read(b);
                if(n==-1) break;
                for (int j = 0; j < n; j++) {
                    System.out.println(n);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

FileInputStream是InputStream子类,InputStream实现Closeable接口。所以,当进入 try 块时,inputStream 被初始化。一旦退出 try 块(无论是因为正常结束还是由于异常),JVM 会自动调用 inputStream 的 close() 方法来关闭文件流。
OutputStream

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

java 复制代码
public class Demo11 {
    public static void main(String[] args) throws FileNotFoundException {
        String content = "Hello, World!";
        try( OutputStream outputStream=new FileOutputStream("D:/Project/java/java-biog/"  +
                "Day20250312/IO测试文件目录/测试1.txt",true)){
            outputStream.write(content.getBytes());
                outputStream.flush();//刷新输出流,确保所有数据都被写出
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

红框里面的true,可以让本次写的内容不覆盖上次的内容,在上次内容后面追加本次的内容!

java 复制代码
public class Demo12 {
    public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("D:/Project/java/java-biog/" +
                "Day20250312/IO测试文件目录/测试1.txt", true)) {
            byte[] b = new byte[]{99, 98, 97};
            outputStream.write(b);
            outputStream.flush();//刷新输出流,确保所有数据都被写出
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
字符流

Reader:

java 复制代码
public class Demo13 {
    public static void main(String[] args) throws FileNotFoundException {
        try(Reader reader=new FileReader("D:/Project/java/java-biog/" +
                "Day20250312/IO测试文件目录/测试1.txt")){
            while(true){
                int ch=reader.read();
                if(ch==-1) break;
                for (int i = 0; i < ch; i++) {
                    System.out.println(ch);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
java 复制代码
public class Demo14 {
    public static void main(String[] args) throws FileNotFoundException {
        try(Reader reader=new FileReader("D:/Project/java/java-biog/" +
                "Day20250312/IO测试文件目录/测试1.txt")){
            while(true){
                char[] chars=new char[1024];
                int ch=reader.read(chars);
                if(ch==-1) break;
                for (int i = 0; i < ch; i++) {
                    System.out.println(chars[i]);
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Writer :

java 复制代码
public class Demo15 {
    public static void main(String[] args) throws FileNotFoundException {
        try(Writer writer=new FileWriter("D:/Project/java/java-biog/"  +
                               "Day20250312/IO测试文件目录/测试1.txt")){
            BufferedWriter bufferedWriter=new BufferedWriter(writer);
            writer.write("hello world");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

BufferedWriter 可以配置为在特定条件下自动刷新缓冲区(例如,当缓冲区满或遇到换行符时)。这确保了数据及时写入,而不需要手动调用 flush() 方法。

3.2 特别注意

  1. 不管字节流或字符流读写文件,结束都需要关闭文件?

每次程序打开一个文件,就会在文件的描述表中(固定长度的顺序表),每次打开文件,就相当申请一个表项。如果光打开,不关闭,后续表项申请完,后续再打开文件,就会打开失败。

  1. 字符流和字节流读相同的字符,输出的字节是不同的?

"你好"这两个字,字节流读取是三个字节,而字符流读取是两个字节。这个是不矛盾的。

  • 在字节流中,对于中文字符"你"和"好",在 UTF-8 编码中,每个字符通常占用3个字节。
  • 对于 UTF-8 编码的文本,Java 会根据编码规则正确地将3个字节的字节序列转换为一个字符。因此,对于"你好"这两个字,字符流会正确地读取两个字(每个中文字符在 UTF-8 中通常占用3个字节,但字符流按字符读取,所以是两个字)。

通常是因为在字节流读取时没有正确处理 UTF-8 的多字节特性,而字符流读取则自动处理了编码,正确地按字符读取。

四、实战演示

4.1 查找删除文件

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

思路:

  1. 先输入扫描文件目录,并判断是否是目录,不是则退出
  2. 输入要删除文件的关键字
  3. traversefile()这个方法是遍历目录下的每个文件
  4. 判断是否是目录,是目录进行递归,不是目录,则判断是否是空文件;如果不是空文件,则进行deletefile()操作是否删除此文件
java 复制代码
public class Demo16 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要扫描的目录:");
        String filename = sc.next();
        File file = new File(filename);
        if (!file.isDirectory()) {
            System.out.println("您输⼊的不是目录,退出");
            return;
        }
        System.out.println("请输入要删除的文件:");
        String deleteName = sc.next();
        traversefile(file, deleteName);
    }

    private static void traversefile(File file, String deleteName) {
        //1.遍历该目录下的文件
        File[] files = file.listFiles();
        //2.判断是否目录是否为空
        if (files == null || files.length == 0) {
            return;
        }
        for (File f : files) {
            //判断是否是目录还是普通文件
            if (f.isDirectory()) {
                traversefile(f, deleteName);
            } else {
                deletefile(f, deleteName);
            }
        }

    }

    private static void deletefile(File f, String deleteName) {
        if (f.getName().contains(deleteName)) {
            System.out.println("找到包含关键字的文件,是否删除"+f.getName()+"文件,(y/n)");
            Scanner sc = new Scanner(System.in);
            String answer = sc.next();
            if (answer.equals("y")) {
                f.delete();
                System.out.println("文件删除!!!");
            } else {
                return;
            }
        }
    }
}

4.2 普通文件的复制

需要让用户指定两个文件路径,一个是源路径 (被复制的文件),一个是目标路径(复制之后生成的文件),打开源路径的文件,读取里面的内容,并写入到目标文件。

思路:

  1. 输入源文件和目标文件路径(判断源文件路径,因为OutputStream会自动创建一个文件)
  2. 运用InputStream和OutputStream从源文件读取写入到目标文件
java 复制代码
public class Demo17 {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入源路径:");
        String filename = sc.next();
        File file = new File(filename);
        System.out.println("请输入目标文件路径:");
        String copyName = sc.next();
        if(!file.isFile()){
            System.out.println("源路径不是一个普通的文件!返回!");
            return;
        }
        try(InputStream in = new FileInputStream(file);
            OutputStream ou=new FileOutputStream(copyName) ){
            while(true){
                byte[] b = new byte[1024];
                int len = in.read(b);
                if(len==-1){
                    break;
                }
                for(int i=0;i<len;i++){
                    ou.write(b[i]);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

4.3 文件内容查找

不仅要找到文件名包含关键字,还要找到文件内容包含关键字

思路:

1.遍历,递归,找到文件名包含关键字的文件并返回文件名

2.判断文件内容,这里用Reader,因为需要将读到的字符拼接起来(Stringbuffer),将拼接起来的字符和关键字对比

java 复制代码
public class Demo18 {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要扫描的目录:");
        String filename = sc.next();
        File file = new File(filename);
        if (!file.isDirectory()) {
            System.out.println("您输⼊的不是目录,退出");
            return;
        }
        System.out.println("请输入关键字:");
        String impName = sc.next();
        traversefile(file, impName);
    }

    private static void traversefile(File file, String impName) throws FileNotFoundException {
        //1.遍历该目录下的文件
        File[] files = file.listFiles();
        //2.判断是否目录是否为空
        if (files == null || files.length == 0) {
            return;
        }
        for (File f : files) {
            //判断是否是目录还是普通文件
            if (f.isDirectory()) {
                traversefile(f, impName);
            } else {
                foudnfile(f, impName);
            }
        }

    }

    private static void foudnfile(File f, String impName) throws FileNotFoundException {
        if(f.getName().contains(impName)) {
            System.out.println("文件名包含关键字"+f.getName());
        }else{
            try(Reader reader= new FileReader(f)) {
                StringBuffer sb = new StringBuffer();
                while(true) {
                    char[] buf = new char[1024];
                    int len= reader.read(buf);
                   if (len == -1) {
                       break;
                   }
                   for(int i=0; i<len; i++) {
                       sb.append(buf[i]);
//                       sb.append(buf, 0, len);
                   }
                   //return stringBuilder.indexOf(word) != -1;
                   // stringBuilder.indexOf()是StringBuffer类中的一个方法,找到关键字word方法会返回 true;反之,返回false
                   if(sb.toString().contains(impName)) {
                       System.out.println(f.getName());
                   }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

此篇博客的全部代码!!!

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
Lizhihao_2 小时前
JAVA-Thread类实现多线程
java·开发语言
编程毕设2 小时前
【含文档+PPT+源码】基于微信小程序的驾考在线学习与测试系统的设计与实现
java·学习·eclipse·tomcat
奔跑的废柴2 小时前
LeetCode 112. 路径总和 II java题解
java·算法·leetcode·二叉树
独孤求败Ace3 小时前
第54天:Web攻防-SQL注入&数据类型&参数格式&JSON&XML&编码加密&符号闭合&复盘报告
xml·java·sql
紫乾20144 小时前
idea cpu干到100%的解决方法?
java·ide·intellij-idea
小安同学iter4 小时前
SpringMVC(三)响应处理
java·spring
努力小贼5 小时前
SpringBoot——Maven篇
java·spring boot·maven
Java开发追求者5 小时前
java 手搓一个http工具类请求传body
java·开发语言·http·调用第三方接口工具
weixin_466485115 小时前
qt5中使用中文报错error: C2001: 常量中有换行符
android·java·qt
onlyzzr6 小时前
Leetcode Hot100 第58题 23.合并K个升序链表
java·算法·leetcode